Skip to main content

Ensure your migrations are linear.

Project description

https://img.shields.io/github/workflow/status/adamchainz/django-linear-migrations/CI/master?style=for-the-badge https://img.shields.io/pypi/v/django-linear-migrations.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

Ensure your migration history is linear.

For a bit of background, see the introductory blog post.

Requirements

Python 3.6 to 3.9 supported.

Django 2.2 to 3.1 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.


Installation

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, run this one-off command for installation:

python manage.py create-max-migration-files

This creates a new max_migration.txt file in each of your first-party apps’ migrations directories and exits. More on those files below…

Usage

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.

System Checks

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>’.

rebase-migration command

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>

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 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 can start to fix the conflict by pulling your latest main branch, then rebasing your titles branch on top of it. When you do this, Git will report the conflict:

$ git switch main
$ git pull
...
$ 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 rebease-migration books
Renamed 0002_longer_titles.py to 0003_longer_titles.py, updated its dependencies, and updated max_migration.txt.

This places the conflcited 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

Note this 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 other parts of the source code as well, such as in the models.

Inspiration

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-linear-migrations-1.1.0.tar.gz (78.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_linear_migrations-1.1.0-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file django-linear-migrations-1.1.0.tar.gz.

File metadata

  • Download URL: django-linear-migrations-1.1.0.tar.gz
  • Upload date:
  • Size: 78.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.25.0 setuptools/51.0.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.9.0

File hashes

Hashes for django-linear-migrations-1.1.0.tar.gz
Algorithm Hash digest
SHA256 1bd089dce04084c709a49e2c7012cabdd7768004d7ed28d41238ab84384f5cdd
MD5 cfe6c0ba9158876cdab2f49942a6ec59
BLAKE2b-256 f22be10e3e758a8e04bab0a3768f47e184611f7f18576d9d62b277721952aa16

See more details on using hashes here.

File details

Details for the file django_linear_migrations-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: django_linear_migrations-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.25.0 setuptools/51.0.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.9.0

File hashes

Hashes for django_linear_migrations-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fcd91b3d685aadfa689576dc624feb2dbfd1c79942f63a289992b6488cbf5bc9
MD5 3be809133c54500c0263c8afe5ccff65
BLAKE2b-256 ab3f8184bffc1282a196f6bccc23f8edc35f70c098c2ad2d4cddcab9122d830d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page