Ensure your migrations are linear.
Ensure your migration history is linear.
For a bit of background, see the introductory blog post.
Python 3.6 to 3.10 supported.
Django 2.2 to 4.0 supported.
Are your tests slow? Check out my book Speed Up Your Django Tests which covers loads of best practices so you can write faster, more accurate tests.
First, install with pip:
python -m pip install django-linear-migrations
Second, add the app to your INSTALLED_APPS setting:
INSTALLED_APPS = [ ..., "django_linear_migrations", ..., ]
The app relies on overriding the built-in makemigrations command. If your project has a custom makemigrations command, ensure the app containing your custom command is above django_linear_migrations, and that your command subclasses its Command class:
# myapp/management/commands/makemigrations.py from django_linear_migrations.management.commands.makemigrations import ( Command as BaseCommand, ) class Command(BaseCommand): ...
Third, check the automatic detection of first-party apps. Run this command:
python manage.py create_max_migration_files --dry-run
This command is for creating max_migration.txt files (more on which later) - in dry run mode it lists the apps it would make such files for. It tries to automatically detect which apps are first-party, i.e. belong to your project. The automatic detection checks the path of app’s code to see if is within a virtualenv, but this detection can sometimes fail, for example on editable packages installed with -e. If you see any apps listed that aren’t part of your project, define the list of first-party apps’ labels in a FIRST_PARTY_APPS setting that you combine into INSTALLED_APPS:
FIRST_PARTY_APPS =  INSTALLED_APPS = FIRST_PARTY_APPS + ["django_linear_migrations", ...]
(Note: Django recommends you always list first-party apps first in your project so they can override things in third-party and contrib apps.)
Fourth, create the max_migration.txt files for your first-party apps by re-running the command without the dry run flag:
python manage.py create_max_migration_files
In the future, when you add a new app to your project, you’ll need to create its max_migration.txt file. Add the new app to INSTALLED_APPS or FIRST_PARTY_APPS as appropriate, then rerun the creation command for the new app by specifying its label:
python manage.py create_max_migration_files my_new_app
django-linear-migrations helps you work on Django projects where several branches adding migrations may be in progress at any time. It enforces that your apps have a linear migration history, avoiding merge migrations and the problems they can cause from migrations running in different orders. It does this by making makemigrations record the name of the latest migration in per-app max_migration.txt files. These files will then cause a merge conflicts in your source control tool (Git, Mercurial, etc.) in the case of migrations being developed in parallel. The first merged migration for an app will prevent the second from being merged, without addressing the conflict. The included rebase_migration command can help automatically such conflicts.
django-linear-migrations comes with several system checks that verify that your max_migration.txt files are in sync. These are:
- dlm.E001: <app_label>’s max_migration.txt does not exist.
- dlm.E002: <app_label>’s max_migration.txt contains multiple lines.
- dlm.E003: <app_label>’s max_migration.txt points to non-existent migration ‘<bad_migration_name>’.
- dlm.E004: <app_label>’s max_migration.txt contains ‘<max_migration_name>’, but the latest migration is ‘<real_max_migration_name>’.
python manage.py create_max_migration_files [app_label [app_label ...]]
This management command creates max_migration.txt files for all first party apps, or the given labels. It’s used in initial installation of django-linear-migrations, and for recreating.
Pass the --dry-run flag to only list the max_migration.txt files that would be created.
Pass the --recreate flag to re-create files that already exist. This may be useful after altering migrations with merges or manually.
This management command can help you fix migration conflicts. Following a conflicted “rebase” operation in your source control tool, run it with the name of the app to auto-fix the migrations for:
$ python manage.py rebase_migration <app_label>
Note rebasing the migration might not always be the correct thing to do. If the migrations in main and feature branches have both affected the same models, rebasing the migration on the end may not make sense. However, such parallel changes would normally cause conflicts in your models files or other parts of the source code as well.
Let’s walk through an example using Git, although it should extend to other source control tools.
Imagine you were working on your project’s books app in a feature branch called titles and created a migration called 0002_longer_titles. Meanwhile a commit has been merged to your main branch with a different 2nd migration for books called 0002_author_nicknames. Thanks to django-linear-migrations, the max_migration.txt file will show as conflicted between your feature and main branches.
You start the fix by reversing your new migration from your local database. This is necessary since it will be renamed after rebasing and seen as unapplied. You do this by switching to the feature branch titles migrating back to the last common migration:
$ git switch titles $ python manage.py migrate books 0001
You then fetch the latest code:
$ git switch main $ git pull ...
You then rebase your titles branch on top of it, for which Git will detect the conflict on max_migration.txt:
$ git switch titles $ git rebase main Auto-merging books/models.py CONFLICT (content): Merge conflict in books/migrations/max_migration.txt error: could not apply 123456789... Increase Book title length Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply 123456789... Increase Book title length
If you look at the contents of the books app’s max_migration.txt at this point, it will look something like this:
$ cat books/migrations/max_migration.txt <<<<<<< HEAD 0002_author_nicknames ======= 0002_longer_titles >>>>>>> 123456789 (Increase Book title length)
It’s at this point you can use rebase_migration to automatically fix the books migration history:
$ python manage.py rebase_migration books Renamed 0002_longer_titles.py to 0003_longer_titles.py, updated its dependencies, and updated max_migration.txt.
This places the conflicted migration on the end of the migration history. It renames the file appropriately, modifies its dependencies = [...] declaration, and updates the migration named in max_migration.txt appropriately.
After this, you should be able to continue the rebase:
$ git add books/migrations $ git rebase --continue
And then migrate your local database to allow you to continue development:
$ python manage.py migrate books Operations to perform: Target specific migration: 0003_longer_titles, from books Running migrations: Applying books.0002_author_nicknames... OK Applying books.0003_longer_titles... OK
I’ve seen similar techniques to the one implemented by django-linear-migrations at several places, and they acted as the inspiration for putting this package together. My previous client Pollen and current client ev.energy both have implementations. This Doordash blogpost covers a similar system that uses a single file for tracking latest migrations. And there’s also a package called django-migrations-git-conflicts which works fairly similarly.
Release history Release notifications | RSS feed
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
|Filename, size||File type||Python version||Upload date||Hashes|
|Filename, size django_linear_migrations-2.2.0-py3-none-any.whl (17.9 kB)||File type Wheel||Python version py3||Upload date||Hashes View|
|Filename, size django-linear-migrations-2.2.0.tar.gz (20.8 kB)||File type Source||Python version None||Upload date||Hashes View|
Hashes for django_linear_migrations-2.2.0-py3-none-any.whl
Hashes for django-linear-migrations-2.2.0.tar.gz