• Stars
    star
    136
  • Rank 267,670 (Top 6 %)
  • Language
    Python
  • License
    MIT License
  • Created about 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Ensure your migration history is linear.

django-linear-migrations

https://img.shields.io/github/actions/workflow/status/adamchainz/django-linear-migrations/main.yml?branch=main&style=for-the-badge https://img.shields.io/badge/Coverage-100%25-success?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.8 to 3.12 supported.

Django 3.2 to 4.2 supported.


Want to work smarter and faster? Check out my book Boost Your Django DX which covers django-linear-migrations and many other tools to improve your development experience.


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, 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

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>'.
  • dlm.E005: Conflicting migrations detected; multiple leaf nodes in the migration graph: <conflicting_migrations>

create_max_migration_files Command

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.

rebase_migration Command

This management command can help you fix migration conflicts. Following a conflicted “rebase” operation in Git, run it with the name of the app to auto-fix the migrations for:

$ python manage.py rebase_migration <app_label>

The command uses the conflict information in the max_migration.txt file to determine which migration to rebase. It automatically detects whether a Git merge or rebase operation is in progress, assuming rebase if a Git repository cannot be found. The command then:

  1. renames the migration
  2. edits it to depend on the new migration from your main branch
  3. updates max_migration.txt.

If Black is installed, the command formats the updated migration file with it, like Django’s built-in migration commands do (from version 4.1+). See below for some examples and caveats.

Note rebasing the migration might not always be the correct thing to do. If the migrations in your main and feature branches have both affected the same models, rebasing the migration to the end may not make sense. However, such parallel changes would normally cause conflicts in your model files or other parts of the source code as well.

Worked Example

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.

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

Then, fetch the latest code:

$ git switch main
$ git pull
...

Next, rebase your titles branch on top of it. During this process, 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

The contents of the books app's max_migration.txt at this point will look something like this:

$ cat books/migrations/max_migration.txt
<<<<<<< HEAD
0002_author_nicknames
=======
0002_longer_titles
>>>>>>> 123456789 (Increase Book title length)

At this point, 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

Code Formatting

rebase_migration does not guarantee that its edits match your code style. If you use a formatter like Black, you’ll want to run it after applying rebase_migration.

If you use pre-commit, note that Git does not invoke hooks during rebase commits. You can run it manually on changed files with pre-commit run.

Branches With Multiple Commits

Imagine the same example as above, but your feature branch has several commits editing the migration. This time, before rebasing onto the latest main branch, squash the commits in your feature branch together. This way, rebase_migration can edit the migration file when the conflict occurs.

You can do this with:

$ git rebase -i --keep-base main

This will open Git’s interactive mode file. Edit this so that every comit after the first will be squashed, by starting each line with “s”. Then close the file, and the rebase will execute.

After this operation, you can rebase onto your latest main branch as per the previous example.

Branches With Multiple Migrations

rebase_migration does not currently support rebasing multiple migrations (in the same app). This is an open feature request, but it is not a priority, since it’s generally a good idea to restrict changes to one migration at a time. Consider merging your migrations into one before rebasing.

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.

More Repositories

1

django-cors-headers

Django app for handling the server headers required for Cross-Origin Resource Sharing (CORS)
Python
5,087
star
2

django-htmx

Extensions for using Django with htmx.
JavaScript
866
star
3

django-upgrade

Automatically upgrade your Django projects.
Python
641
star
4

django-mysql

🐬 🐴 Extensions to Django for use with MySQL/MariaDB
Python
535
star
5

blacken-docs

Run `black` on python code blocks in documentation files
Python
513
star
6

time-machine

Travel through time in your tests.
Python
447
star
7

flake8-comprehensions

❄️ A flake8 plugin to help you write better list/set/dict comprehensions.
Python
446
star
8

django-perf-rec

Keep detailed records of the performance of your Django code.
Python
330
star
9

django-browser-reload

Automatically reload your browser in development.
Python
296
star
10

mac-ansible

🐄 Configuring my mac with Ansible
Shell
170
star
11

patchy

⚓ Patch the inner source of python functions at runtime.
Python
163
star
12

apig-wsgi

Wrap a WSGI application in an AWS Lambda handler function for running on API Gateway or an ALB.
Python
146
star
13

treepoem

Barcode rendering for Python supporting QRcode, Aztec, PDF417, I25, Code128, Code39 and many more types.
PostScript
115
star
14

ec2-metadata

An easy interface to query the EC2 metadata API, with caching.
Python
102
star
15

django-rich

Extensions for using Rich with Django.
Python
90
star
16

django-permissions-policy

Set the draft security HTTP header Permissions-Policy (previously Feature-Policy) on your Django app.
Python
81
star
17

django-watchfiles

Use watchfiles in Django’s autoreloader.
Python
81
star
18

django-read-only

Disable Django database writes.
Python
75
star
19

django-minify-html

Use minify-html, the extremely fast HTML + JS + CSS minifier, with Django.
Python
73
star
20

flake8-tidy-imports

❄️ A flake8 plugin that helps you write tidier imports.
Python
60
star
21

heroicons

Use heroicons in your Django and Jinja templates.
Python
58
star
22

SublimeFiglet

Add in ASCII text art from "figlet"
Python
46
star
23

lifelogger

📅 Track your life like a pro on Google Calendar via your terminal.
Python
40
star
24

pip-lock

Check for differences between requirements.txt files and your environment
Python
36
star
25

django-capture-on-commit-callbacks

Capture and make assertions on transaction.on_commit() callbacks.
Python
35
star
26

django-version-checks

System checks for your project's environment.
Python
34
star
27

django-jsonfield

(Maintenance mode only) Cross-database JSON field for Django models.
Python
30
star
28

unittest-parametrize

Parametrize tests within unittest TestCases.
Python
29
star
29

scripts

Useful little scripts that I use on commandline. Work in OS-X + zsh at least.
Shell
27
star
30

multilint

✅ Run multiple python linters easily
Python
27
star
31

flake8-no-pep420

A flake8 plugin to ban PEP-420 implicit namespace packages.
Python
22
star
32

django-startproject-templates

Python
22
star
33

pytest-is-running

pytest plugin providing a function to check if pytest is running.
Python
21
star
34

SublimeHTMLMustache

✏️ Adds HTML Mustache as a language to Sublime Text 2/3, with snippets.
19
star
35

pytest-reverse

Pytest plugin to reverse test order.
Python
19
star
36

owela-club

Play the Namibian game of Owela against a terrible AI. Built using Django and htmx.
Python
18
star
37

nose-randomly

👃 Nose plugin to randomly order tests and control `random.seed`
Python
17
star
38

talk-how-to-hack-a-django-website

JavaScript
14
star
39

dynamodb_utils

A toolchain for Amazon's DynamoDB to make common operations (backup, restore backups) easier.
Python
12
star
40

sound-resynthesis

🔈 Sound Resynthesis with a Genetic Algorithm - my final year project from university
Java
12
star
41

mariadb-dyncol

💾 Python dicts <-> MariaDB Dynamic Column binary format
Python
11
star
42

pre-commit-oxipng

Mirror of oxipng for pre-commit.
Rust
11
star
43

pytest-flake8dir

❄️ A pytest fixture for testing flake8 plugins.
Python
11
star
44

logentries-cli

📒 Get your logs from Logentries on the comandline.
Python
10
star
45

pre-commit-dprint

Mirror of dprint for pre-commit.
9
star
46

sublime-rst-improved

Python
8
star
47

h

Python
8
star
48

talk-improve-startup-time

“How to profile and improve startup time” talk
JavaScript
8
star
49

sublime_text_settings

✏️ My settings for sublime text 3 - as in Packages/User
Python
8
star
50

talk-django-and-htmx

JavaScript
7
star
51

django-settings-file

Python
7
star
52

tox-py

Adds the --py flag to tox to run environments matching a given Python interpreter.
Python
6
star
53

kwargs-only

A decorator to make a function accept keyword arguments only, on both Python 2 and 3.
Python
6
star
54

pytest-super-check

🔒 Pytest plugin to ensure all your TestCase classes call super() in setUp, tearDown, etc.
Python
6
star
55

django_atomic_celery

Atomic transaction aware Celery tasks for Django
Python
6
star
56

django-coverage-example

Python
5
star
57

django-pymysql-backend

A Django database backend for MySQL using PyMySQL.
Python
5
star
58

pytest-restrict

🔒 Pytest plugin to restrict the test types allowed
Python
5
star
59

talk-data-oriented-django

JavaScript
4
star
60

talk-speed-up-your-tests-with-setuptestdata

JavaScript
4
star
61

talk-django-and-web-security-headers

JavaScript
3
star
62

fluentd.tmLanguage

Syntax highlighting for Fluentd configuration files
3
star
63

django-ticket-33153

https://code.djangoproject.com/ticket/33153
Python
3
star
64

pytest-flake8-path

A pytest fixture for testing flake8 plugins.
Python
3
star
65

django_atomic_signals

Signals for atomic transaction blocks in Django 1.6+
Python
3
star
66

flake8-no-types

A flake8 plugin to ban type hints.
Python
3
star
67

SublimeMoveTabs

✏️ A short plugin for Sublime Text 2 that allows rearrangement of tabs/'views' with the keyboard.
Python
3
star
68

dynamodb_local_utils

Automatically run DynamoDB Local on Mac OS X
Shell
3
star
69

pygments-git

Pygments lexers for Git output and files
Python
3
star
70

workshop-evenergy-concurrency-and-parallelism

Python
2
star
71

talk-how-complex-systems-fail

Talk for the Papers We Love London meetup
TeX
2
star
72

google_lifelog

Making a lifelog on google calendar.
Python
2
star
73

talk-building-interactive-pages-with-htmx

JavaScript
2
star
74

workshop-idiomatic-python

Python
2
star
75

ansible-talk-custom-template-filters

My talk for the Ansible London Meetup in March 2015
TeX
2
star
76

talk-django-vs-flask

JavaScript
2
star
77

django-talk-factory-boy

Talk for London Django Meetup
TeX
2
star
78

ProgrammingInterview

Solving the problems posted on ProgrammingInterview on YouTube
Python
2
star
79

example-pre-commit-ci-lite

example
2
star
80

django-server-push-demo

Python
2
star
81

talk-django-capture-on-commit-callbacks

JavaScript
2
star
82

techblog

Filled with little coding notes and fixes.
2
star
83

adamchainz

👋
Python
2
star
84

SublimeCowsay

✏️🐮 A silly little Sublime Text plugin for 2 and 3 to allow you to quickly convert a text selection to a cow speech bubble via the brilliant cowsay utility.
Python
2
star
85

djceu2019-workshop

Python
2
star
86

talk-what-happens-when-you-run-manage.py-test

JavaScript
2
star
87

talk-technologies-that-will-be-around-in-21-years

JavaScript
2
star
88

django-demo-constraint-single-column-not-null

Python
2
star
89

django-harlequin

Launch Harlequin, the SQL IDE for your Terminal, with your Django database configuration.
Python
2
star
90

channels-bug-connection-closed

Reproduction for Channels bug
Python
1
star
91

workshop-concurrency-and-parallelism

Python
1
star
92

django-talk-duth

Django Under The Hood 2015 Summary
TeX
1
star
93

workshop-rest-api-django

Python
1
star
94

workshop-recommended-practices

Python
1
star
95

talk-django-3.2-test-features

JavaScript
1
star
96

workshop-profiling-and-debugging

Python
1
star
97

django-feature-policy-shim

1
star
98

kvkit

high-level python toolkit for ordered key/value stores
Python
1
star
99

phabricator-csv-import

Python
1
star
100

django-blue-green-example

Reproducing the technique from “Smooth Database Changes in Blue-Green Deployments” by Mariusz Felisiak.
Python
1
star