Open-Source Wikis

/

Django

/

Systems

/

Migrations

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

Key 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 --> File
graph 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 --> Recorder

State 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:

  1. Compute the schema state at each migration in the plan.
  2. Pass an app_labelProjectState snapshot to operations that need it (e.g., RunPython gets a historical apps argument 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_MODEL and other swappable models are tracked specially.
  • Cross-app dependencies. A ForeignKey to 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 in django/core/management/commands/.
  • System checksdjango/core/checks/migrations.py warns about apps that have models but no migrations.
  • ContentType (django/contrib/contenttypes/) listens to post_migrate to keep its rows in sync with the model registry.

Entry points for modification

  • New operation type: subclass Operation in django/db/migrations/operations/base.py. Implement state_forwards, database_forwards, database_backwards, describe, and deconstruct. Add to the operations module's __init__.py.
  • Improve the autodetector: edit django/db/migrations/autodetector.py. Run tests/migrations/test_autodetector.py continuously. Be prepared for surprising heuristics.
  • Optimiser changes: django/db/migrations/optimizer.py. Add tests under tests/migrations/test_optimizer.py.
  • Squashing: changes to squashmigrations live in django/core/management/commands/squashmigrations.py and 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.

Migrations – Django wiki | Factory