Database Migrations are Django’s version-control system for database schema, represented as Python files containing Operations (CreateModel, AddField, AlterField, RunSQL, RunPython) that transform the database from one state to another, tracked in the django_migrations table to ensure idempotent application across environments.
Migrations solve the problem of evolving database schema alongside code without manual SQL scripts or data loss. When models change, makemigrations generates a new migration file by comparing current models to the historical state reconstructed from previous migrations. migrate applies unapplied migrations in dependency order, recording each in django_migrations. This enables team collaboration, CI/CD integration, and safe rollbacks (when operations are reversible).
- Models changed — Developer modifies
models.py(add field, change type, new model) - Autodetector runs —
makemigrationscompares current models to project state from last migration - Operations generated — Creates
Migrationclass withoperations = [AddField(...), ...] - Dependencies resolved — Migration declares
dependencies = [('app', '0001_initial'), ...] - Migration applied —
migrateruns operations in topological order, updatesdjango_migrations - State reconstructed — Future
makemigrationsreplays all migrations to compute current state
- Declarative operations:
CreateModel,AddField,AlterField,RemoveField,RunSQL,RunPython - Dependency graph: Linear per-app, cross-app via
dependencies; topological sort ensures order - Reversibility: Most operations auto-reversible;
RunPythonneedsreverse_code;RunSQLneeds reverse SQL - Squashing:
squashmigrationscombines many migrations into one for faster initial setup - Historical models:
apps.get_model('app', 'Model')inRunPythonuses frozen model state
- Built from: ORM — Source of schema changes
- Built from: Historical Model State — Baseline for autodetection
- Builds into: Schema Operations — Individual migration steps
- Builds into: Data Migrations —
RunPythonfor data transformation - Builds into: Squash Migrations — Optimize migration history
- Contrasts with: Alembic — SQLAlchemy’s migration tool, similar but separate ecosystem
- Contrasts with: Flyway — SQL-file based, not ORM-coupled
- Related: Transaction Wrapper — Each migration in transaction (except PostgreSQL DDL)
- Related: RunPython — Custom data migration logic
- Non-reversible migrations:
RunPythonwithoutreverse_codeblocks rollback;migrate --fakerisky - Concurrent migrations: Two developers create
0003_...— resolve withmakemigrations --merge - Large table ALTER: Adding column with default locks table; use
AddField→RunSQL(no default) →AlterField - Historical model drift:
RunPythonusing current model instead ofapps.get_model()breaks future migrations - Swap app models:
swappable = 'AUTH_USER_MODEL'requires special handling in migrations