Question
Have you ever encountered CommandError: Conflicting migrations detected;
multiple leaf nodes in the migration graph:
? You definetely have if you work on a team developing single django application.
How does it happen?
To understand the error, we need to analyze what Django does with migrations under the hood. Official Django Migration Documentation has in-depth explanation but long story short migrations remember ancestors and descendants and they have to be linearly ordered. There should not be a migration with multiple decendants that do not have child migrations (leaf nodes) in an app. This helps to rolback to any migration later on.
Example
Imagine you are in a team building Book Store website. You have books
Django app and Book
model inside it. Let’s say you are working on feature/book_isbn
branch. You added isbn
field to Book
model and run python manage.py makemigrations
. Meanwhile, your teammate is working on feature/book_cover_photo
branch and added cover_photo
field to Book
model and a new migration for it.
To illustrate this have a look on this flowchart:
You both have common migration 0002_book_author
but conflicting migration 0002_book_isbn
and 0002_book_cover_photo
respectively. When both branches are merged to develop
and python manage.py migrate
is run Bang! You get the CommandError: Conflicting migrations ...
Solution
Option 1
Django suggests to run python manage.py makemigration --merge
to resolve it. This will create another migration that merges leaf migrations.
Your migrations graph now looks like this:
Run python manage.py migrate
again and everything is fine, Yeeaah!
I personally don’t like having extra merge migration file. Also Django will probably not always merge migrations correctly. There has to be a better way.
Option 2
Checkout to develop
branch that feature/book_isbn
and feature/book_cover_photo
merged in. Remove your migration file 0002_book_isbn
from migrations folder. Rollback to the most recent common migration between the branches in this case 0002_book_author
by running python manage.py books 0002_book_author
. Now you can create a new migration for Book.isbn
. Just run python manage.py makemigrations books
again. You will have 0004_book_isbn
migration like in flowchart below.
How to detect the conflict
Continious Integration at hand, we can check any migration conflicts and fail a build without stupidly trying to apply migrations to our database. This CLI command lists all conflicting migrations exist in the project. Run it from the root of your project.
find . -type f -name "*.py"|grep -o ".*/migrations/[0-9]\+"|sort|uniq -c|awk '$1 > 1 {print $0}
Here is an example in Bitbucket-Piplines:
image: python:3.6
pipelines:
default:
...
- step:
name: Apply migrations
script:
...
# Check any conflicting migrations, fail if so
- CONFLICTING_MIGRATIONS=$(find . -type f -name "*.py" | grep -o ".*/migrations/[0-9]\+" | sort | uniq -c | awk '$1 > 1 {print $0}')
- if [ -n "${CONFLICTING_MIGRATIONS}" ]; then echo "CONFLICTING MIGRATIONS:${CONFLICTING_MIGRATIONS}"; exit 1; fi;
...
You can put it in your CI pipeline like Jenkins, Travis etc.
In a nutshell
Django migrations are powerfull, magical tool to automate database operations. You have to know ins and outs of it. I hope you get something amazing from this post. Now go & build some cool Django apps.
Feedback & Comment
Your feedbacks and comments are most appreciated. Please write your opinion on this topic, share your experiance in the comment section below.