skip to content

In Django, what do migrate --fake and migrate --fake-initial do, and when is each safe to use?

level: middleimportance: should knowfreq 45%

answer

  1. bookkeeping without SQL
  2. the history table only
  3. tables that already exist
  4. initial migrations only
  5. the schema must truly match

basics

~20 s

migrate --fake records migrations as applied in django_migrations without running their SQL. --fake-initial fakes an app's initial migration only when its CreateModel tables and AddField columns already exist. Both are safe only when the schema really matches the migrations.

solid answer

~40 s

`--fake` marks the targeted migrations as applied (or, when moving backwards, as unapplied) in `django_migrations` without running any operation. It is for aligning the history with a schema you changed by other means, such as a column a database administrator already added. `--fake-initial` is narrower: for migrations marked `initial` it checks whether every table from its `CreateModel` operations and every column from its `AddField` operations already exists, fakes it if so, and runs normally otherwise. It exists for adopting migrations on a database created before them. Neither checks column types, constraints or indexes, so a wrong fake leaves Django believing in a schema that is not there, and the error appears later as a missing column or a failed migration.

code

bash · 10 lines
bash
# The column was added by hand during an incident; record 0004 without running it
python manage.py sqlmigrate workouts 0004   # compare with the live schema first
python manage.py migrate workouts 0004 --fake

# A migration was faked by mistake; run it for real
python manage.py migrate workouts 0003 --fake
python manage.py migrate workouts

# Adopting migrations on a pre-existing database
python manage.py migrate --fake-initial

go deeper

for a junior

Know that --fake only records a migration as applied without changing the schema, and that it is not a routine command.

for a middle

Explain how --fake-initial decides, what it checks and does not check, and how to fake backwards to re-run a migration.

for a senior

Verify the schema before any fake, recognise the symptoms of a bad one, and repair the history without losing data.

for a principal

Set a rule for when faking in production is allowed, who approves it, and how schema changes made outside migrations are brought back under them.

## What faking means `migrate` normally does two things per migration: runs its operations against the database, then inserts a row into `django_migrations`. **Faking** does only the second part. The table says the migration ran; the schema is untouched. Django's documentation calls `--fake` a tool for advanced users and warns that it can put the migration state table into a state that needs manual recovery. It is a bookkeeping tool, not a way to skip problems. ## --fake ```bash python manage.py migrate workouts 0004 --fake ``` - Moving **forwards**, it records every migration up to the target as applied without running them. - Moving **backwards** (a target earlier than what is applied), it deletes the rows without running the reverse operations. - It applies to whatever the plan would be, so check with `--plan` first. Legitimate uses: 1. **Schema already changed by other means.** A database administrator added the `avg_heart_rate` column during an incident. The migration that adds it would fail with a duplicate column, so fake it once the schema matches exactly. 2. **Repairing a broken history.** A migration's SQL ran but recording failed, or rows were deleted by mistake; faking restores the record. 3. **Re-running one migration for real.** Fake back to the migration before it, then migrate forward normally: `migrate workouts 0003 --fake` followed by `migrate workouts`. ## --fake-initial `--fake-initial` targets one situation: a database whose tables were created **before** the app had migrations, such as a legacy schema being brought under Django's migration framework. For each migration marked `initial` (usually `0001_initial`), Django checks: - every table created by its `CreateModel` operations exists, and - every column added by its `AddField` operations exists. If all do, the migration is faked; if any is missing, it runs normally. Proxy models, unmanaged models and models a database router does not allow on this database are ignored in the check. ## What neither option checks | Checked | Not checked | |---|---| | table names (`--fake-initial`) | column types and lengths | | column names for `AddField` (`--fake-initial`) | `NULL` or `NOT NULL` | | nothing at all (`--fake`) | indexes, constraints, defaults | | | data migrations that should have run | So `--fake-initial` against a table with the right name but an outdated shape reports success, and the first query touching a missing column fails in production. ## Recognising a bad fake - `showmigrations` shows `[X]`, yet queries fail with a missing column: the migration was faked while the schema did not match. - `migrate` fails with `InconsistentMigrationHistory` (*Migration X is applied before its dependency Y*): a later migration was faked while an earlier one was not recorded. - `sqlmigrate workouts 0004` shows the statements the fake skipped, which is what you compare against the actual schema before repairing it. Repair means making the schema and the record agree again: either run the missing SQL by hand and keep the record, or fake the record back and let `migrate` apply it properly. ## A worked example During an incident a database administrator runs an `ALTER TABLE` that adds `avg_heart_rate` to the workouts table. Later a developer adds the same field to the `Workout` model, and `makemigrations` writes `0004_workout_avg_heart_rate` with an `AddField`. Applying it would fail, because the column already exists. 1. Run `sqlmigrate workouts 0004` to see the exact column definition Django would create. 2. Compare it with the real column in `dbshell`: the type, whether it allows `NULL`, and any default or constraint. 3. If they differ, make the database match the migration first (or change the model so the migration matches the database). 4. Only when they are identical, run `migrate workouts 0004 --fake`. 5. Run `showmigrations workouts` to confirm the record, and let later migrations apply normally. Skipping steps 2 and 3 is how a fake turns into a production bug months later, when a migration that alters the column assumes a definition the table never had. ## A rule of thumb Fake only after you have verified, table by table and column by column, that the database already looks exactly as the migration would leave it. If you cannot say that with confidence, run the migration instead, or write a new migration that makes the schema match.

  • A migration was faked by mistake and its column is missing; how do you apply it for real?
    Move the record back without touching the schema, then migrate forward normally: `migrate workouts 0003 --fake` deletes the rows for later migrations, and `migrate workouts` then runs `0004` and anything after it for real. Check with `--plan` first, since migrations in other apps that depend on them are moved too.
  • Why does --fake-initial not help after adding a column to an existing app?
    It only considers migrations marked `initial`, normally `0001_initial`. A later `AddField` migration is never faked by it; if the column already exists, that migration fails, and you either fake that specific migration with `--fake` after checking the schema, or rewrite the change so the state and the database agree.

saying these in an interview costs you the question

  • --fake runs the SQL in a transaction and then rolls it back.
  • --fake-initial checks that column types match the migration.
  • --fake-initial fakes any migration whose tables already exist.
  • Faking a migration is a safe way to skip one that fails.
  • migrate --fake leaves the django_migrations table unchanged.