django/django
Migrations
django/db/migrations/ is the schema migration system. Migrations are Python files that describe one step of schema or data evolution; the framework can autogenerate them from model changes, apply them in dependency order, and roll them back when feasible.
Purpose
Track every change to the database schema as code, in version control, with an explicit dependency graph. The migration system handles autodetection (diff your model state against the last migration), graph resolution (apply migrations in topological order), DDL generation (delegate to the schema editor), and reversibility.
Directory layout
django/db/migrations/
├── __init__.py
├── migration.py # Migration base class
├── operations/ # Operation classes (CreateModel, AddField, RunPython, …)
│ ├── base.py
│ ├── models.py # CreateModel, DeleteModel, RenameModel, AlterModelOptions, …
│ ├── fields.py # AddField, AlterField, RemoveField, RenameField
│ ├── special.py # RunPython, RunSQL, SeparateDatabaseAndState
│ └── ...
├── autodetector.py # MigrationAutodetector (the diff-and-emit engine)
├── executor.py # MigrationExecutor (orchestrates apply/unapply)
├── graph.py # MigrationGraph (DAG of migrations and dependencies)
├── loader.py # MigrationLoader (reads migration files, builds graph)
├── recorder.py # MigrationRecorder (django_migrations table)
├── state.py # ProjectState, ModelState (in-memory representation)
├── questioner.py # Interactive prompts for ambiguous cases
├── optimizer.py # Combines redundant operations during squash
├── writer.py # Renders an in-memory Migration to source code
├── serializer.py # Pickles model field/operation values to source
└── exceptions.pyKey abstractions
| Type | File | Role |
|---|---|---|
Migration |
migration.py |
A single migration with dependencies and operations |
Operation |
operations/base.py |
One reversible step (AddField, RunPython, …) |
MigrationGraph |
graph.py |
DAG of migrations; supports forward/backward plans |
MigrationLoader |
loader.py |
Reads <app>/migrations/*.py and builds the graph |
MigrationExecutor |
executor.py |
Applies/unapplies a plan; manages the migration recorder |
MigrationAutodetector |
autodetector.py |
Diffs from_state against to_state and emits operations |
MigrationRecorder |
recorder.py |
Reads/writes the django_migrations table |
ProjectState |
state.py |
In-memory snapshot of all models at a given migration |
ModelState |
state.py |
In-memory snapshot of a single model |
MigrationQuestioner |
questioner.py |
Asks the user (or returns defaults) for ambiguous cases |
MigrationOptimizer |
optimizer.py |
Combines AddField + AlterField etc. during squashing |
MigrationWriter |
writer.py |
Pretty-prints a Migration back to a .py file |
How it works
graph TD
Models["Model classes<br/>(via apps registry)"]
Loader["MigrationLoader<br/>read existing migrations"]
FromState["ProjectState (from history)"]
ToState["ProjectState (from current models)"]
Auto["MigrationAutodetector"]
Ops["[Operation, ...]"]
Writer["MigrationWriter"]
File["<app>/migrations/0042_xyz.py"]
Models --> ToState
Loader --> FromState
FromState --> Auto
ToState --> Auto
Auto --> Ops
Ops --> Writer
Writer --> Filegraph TD
File["migration files"]
Loader["MigrationLoader"]
Graph["MigrationGraph<br/>topological sort"]
Recorder["MigrationRecorder<br/>(django_migrations table)"]
Plan["[(migration, backwards), ...]"]
Executor["MigrationExecutor"]
Schema["BaseDatabaseSchemaEditor<br/>(per-backend overrides)"]
DB[(Database)]
File --> Loader
Loader --> Graph
Recorder --> Graph
Graph --> Plan
Plan --> Executor
Executor --> Schema
Schema --> DB
Executor --> RecorderState replay
The autodetector and executor both rely on state replay. Given a sequence of migration operations, you can produce a ProjectState by applying each Operation.state_forwards() to a starting state. This means the framework reasons about model state without actually touching the database: ProjectState is just dicts of ModelState objects.
For the autodetector, from_state is the result of replaying every existing migration; to_state is built from the current model classes via the apps registry. The diff between the two becomes the new migration.
For the executor, state replay is used to:
- Compute the schema state at each migration in the plan.
- Pass an
app_label→ProjectStatesnapshot to operations that need it (e.g.,RunPythongets a historicalappsargument so functions can use models as they existed at that migration).
The autodetector is the hardest part
autodetector.py is over 2,000 lines because diffing model state is genuinely hard:
- Renames vs delete-then-add. When a field disappears and a new one appears, is it a rename? The autodetector uses heuristics and falls back to
MigrationQuestioner.ask_rename()when ambiguous. - Swappable models.
AUTH_USER_MODELand other swappable models are tracked specially. - Cross-app dependencies. A
ForeignKeyto another app's model creates a migration dependency. - Operation ordering. A field that references a not-yet-created model has to be deferred.
If you're modifying the autodetector, run tests/migrations/test_autodetector.py after every change.
Squashing
squashmigrations calls MigrationOptimizer.optimize() to combine sequential operations. AddField + AlterField becomes a single AddField with the final config. CreateModel followed by DeleteModel cancels out. The optimiser is conservative — it only combines operations it knows are safe.
Built-in operations
The operations module is the public API for migrations:
| Operation | File | What it does |
|---|---|---|
CreateModel |
operations/models.py |
Creates a table |
DeleteModel |
operations/models.py |
Drops a table |
RenameModel |
operations/models.py |
Renames a table |
AlterModelTable |
operations/models.py |
Changes db_table |
AlterUniqueTogether |
operations/models.py |
Modifies unique_together |
AlterIndexTogether |
operations/models.py |
Modifies index_together |
AlterModelOptions |
operations/models.py |
Pure metadata change (no DDL) |
AlterModelManagers |
operations/models.py |
Updates managers (no DDL) |
AddField |
operations/fields.py |
Adds a column |
RemoveField |
operations/fields.py |
Drops a column |
AlterField |
operations/fields.py |
Changes a column |
RenameField |
operations/fields.py |
Renames a column |
AddIndex |
operations/models.py |
Creates an index |
RemoveIndex |
operations/models.py |
Drops an index |
AddConstraint |
operations/models.py |
Adds a check/unique constraint |
RemoveConstraint |
operations/models.py |
Drops a constraint |
RunPython |
operations/special.py |
Calls a Python function |
RunSQL |
operations/special.py |
Executes raw SQL |
SeparateDatabaseAndState |
operations/special.py |
Apply state without DDL or vice versa |
The recorder table
MigrationRecorder manages a django_migrations table (created on first migrate). Each row is (app_label, name, applied_at). The graph is consulted to figure out what's applied, what's missing, and what's been deleted from the codebase but is still in the database.
Integration points
- The ORM (orm) — migrations consume model state through the apps registry and emit DDL via the orm's schema editor.
- Management commands (management) —
makemigrations,migrate,showmigrations,sqlmigrate,optimizemigration,squashmigrations. All indjango/core/management/commands/. - System checks —
django/core/checks/migrations.pywarns about apps that have models but no migrations. ContentType(django/contrib/contenttypes/) listens topost_migrateto keep its rows in sync with the model registry.
Entry points for modification
- New operation type: subclass
Operationindjango/db/migrations/operations/base.py. Implementstate_forwards,database_forwards,database_backwards,describe, anddeconstruct. Add to the operations module's__init__.py. - Improve the autodetector: edit
django/db/migrations/autodetector.py. Runtests/migrations/test_autodetector.pycontinuously. Be prepared for surprising heuristics. - Optimiser changes:
django/db/migrations/optimizer.py. Add tests undertests/migrations/test_optimizer.py. - Squashing: changes to
squashmigrationslive indjango/core/management/commands/squashmigrations.pyand the optimiser.
Key source files
| File | Purpose |
|---|---|
django/db/migrations/autodetector.py |
The diff engine (~2,066 lines) |
django/db/migrations/state.py |
ProjectState, ModelState |
django/db/migrations/executor.py |
Apply/unapply with the schema editor |
django/db/migrations/loader.py |
Read migrations, build graph |
django/db/migrations/graph.py |
DAG operations |
django/db/migrations/operations/models.py |
Model-level operations |
django/db/migrations/operations/fields.py |
Field-level operations |
django/db/migrations/writer.py |
Render migrations to source |
django/db/migrations/serializer.py |
Pickle helpers for the writer |
django/db/migrations/optimizer.py |
Squash optimisation |
django/db/migrations/questioner.py |
Interactive prompts |
django/db/migrations/recorder.py |
django_migrations table |
django/core/management/commands/migrate.py |
Apply migrations |
django/core/management/commands/makemigrations.py |
Generate migrations |
django/core/management/commands/squashmigrations.py |
Squash migrations |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.