Django Migrations

Django migrations are versioned Python files that describe changes to your database schema: adding a table, a column, an index, a constraint. makemigrations generates them by comparing your models with the previous migration state, and migrate applies them in dependency order, recording what's been applied in the django_migrations table. The schema becomes code that's reviewed, versioned, and reproducible on every developer machine, CI run, and production database.

Generating migrations is easy. Running them safely against a busy production database is the real skill: some operations lock tables, some changes break the old code still running during a deploy, and data backfills can take hours. See database migrations for the general principles; this page covers the Django specifics.

TL;DR

Quick Example

Adding a field, backfilling it, then making it required, across three migrations:

Core Concepts

Generating and Applying

Migration Files and Dependencies

Each migration lists dependencies (the previous migration in the app, plus migrations in other apps it relies on) and operations (CreateModel, AddField, AlterField, RemoveField, AddIndex, AddConstraint, RunPython, RunSQL…). Django builds a graph from dependencies and applies migrations in topological order. Migrations are applied atomically in a transaction on databases that support transactional DDL, such as PostgreSQL.

Data Migrations

RunPython(forward, reverse) runs Python code during migration. Inside it, use apps.get_model("app", "Model") to get the historical version of the model as it existed at that point in migration history. Importing the current model class directly breaks when the model later changes. Provide a reverse function (or RunPython.noop) so migrations can be rolled back. RunSQL does the same for raw SQL.

Merge Conflicts

When two branches each add 0015_* migrations to the same app, Django detects multiple leaf nodes and refuses to migrate. Either run makemigrations --merge, which creates a migration depending on both, or delete your branch's unmerged migration, rebase, and regenerate. The latter keeps history linear.

Squashing

Apps accumulate hundreds of migrations over the years, slowing down test database creation. squashmigrations app 0001 0120 combines them into one migration with replaces. Once all environments have applied the originals, delete them and remove replaces. Handwritten RunPython operations need review during squashing, since they may be optimized away or need manual handling.

Zero-Downtime Migrations

During a rolling deploy, old and new application code run simultaneously against one schema. Migrations must be compatible with both:

On PostgreSQL, set a lock_timeout for migrations so a blocked ALTER TABLE fails fast instead of queueing behind long transactions and stalling all traffic. Tools like django-pg-zero-downtime-migrations and linters like django-migration-linter flag unsafe operations automatically. See deployment strategies.

Best Practices

Review Every Migration's SQL

Auto-generated doesn't mean safe. Check sqlmigrate output for table rewrites, locks, and full-table scans before merging. Pay special attention to AlterField on large tables.

Keep Schema and Data Migrations Separate

Mixing a schema change and a long data backfill in one migration holds locks for the backfill's duration. Split them, and move big backfills into batched jobs.

Run Migrations as a Deploy Step

Apply migrations once per deploy, from a release job or pre-deploy hook, not from every app instance at startup, where concurrent migrate runs can race.

Test Migrations Against Realistic Data

A migration that takes 50 ms on a dev database can take 40 minutes on a 200-million-row production table. Test on a production-sized copy or snapshot for anything touching large tables.

Common Mistakes

Importing Models in Data Migrations

Editing Applied Migrations

Changing a migration that has already run in some environment makes the schema diverge silently, since Django won't re-run it. Create a new migration instead.

Adding a NOT NULL Column Without a Default to a Big Table

makemigrations prompts for a one-off default and generates a migration that may rewrite or lock the whole table on older databases, and old code inserting rows without the field will fail. Add it nullable or with db_default, backfill, then tighten.

FAQ

Should migrations be committed to version control?

Yes. Migrations are part of your codebase: every environment must apply the same migrations in the same order. Never generate them on the server at deploy time.

How do I roll back a migration?

python manage.py migrate app 0013 unapplies later migrations using their reverse operations. It only works if each migration is reversible (RunPython with a reverse function, and no irreversible data loss). In production, fixing forward with a new migration is often safer than rolling back.

How do I fake a migration?

migrate app 0014 --fake marks it applied without running it, which is useful when the schema was changed manually or when adopting existing tables (--fake-initial). Use it carefully: a faked migration that doesn't match reality causes confusing errors later.

How do I handle migrations across multiple apps?

Declare cross-app dependencies in each migration's dependencies; makemigrations does this automatically for ForeignKeys. For custom user models, depend on the user app's initial migration and reference settings.AUTH_USER_MODEL via migrations.swappable_dependency.

Related Topics

References