Migrations Deep Dive: makemigrations & migrate

A migration is a versioned Python file that records a change to your models so Django can apply that exact change to the database schema in the right order, on every machine.

Learn Migrations Deep Dive: makemigrations & migrate in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice…

Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll see exactly what migration files contain, how the migration graph orders them through dependencies, how to move data with RunPython, and how the escape hatches --fake, sqlmigrate, and squashing fit in.

Working with the schema is a two-step dance. makemigrations compares your models.py to the last known state and writes a new migration file describing the difference — it never touches the database. migrate then reads the unapplied migration files and runs their SQL against the database. Think plan , then apply .

The generated file is plain, readable Python — a list of operations and the migrations it depends on:

Migrations are not a flat list — they form a directed graph . Each file's dependencies point at the migrations that must run first. Django performs a topological sort of that graph to decide the real order, which is how a foreign key in one app can safely depend on a table created in another app.

Beyond schema changes, migrations can move data . A RunPython operation runs a function during migrate , giving you a historical version of the model to backfill or transform rows. You write a forwards function and, ideally, a reverse one so the migration can be rolled back.

Fill in the blank so migrate applies only the migrations that are not already in the applied list.

❌ "You are trying to add a non-nullable field without a default"

You added a required field to a table that already has rows, so Django can't fill the gap.

✅ Fix: give the field a default=... , or make it null=True , then re-run makemigrations .

❌ "Conflicting migrations detected; multiple leaf nodes"

Two branches both created a 0002 migration, so the graph forked.

✅ Fix: run python manage.py makemigrations --merge to create a migration that depends on both leaves.

❌ "Table already exists" after adopting an existing database

The schema is already there, but Django still wants to run the initial migration.

✅ Fix: run python manage.py migrate --fake-initial to record it as applied without recreating tables.

Squashing replays a list of operations and keeps the final column set, dropping intermediate churn. Write the optimizer that produces the squashed schema.

Lesson complete — you understand the whole migration pipeline!

You can now read a migration file, explain why makemigrations plans and migrate applies, follow the dependency graph, write a RunPython data migration, and reach for sqlmigrate , --fake , and squashing when you need them.

🚀 Up next: Model Fields & Options — the building blocks that every migration is made of.

Practice quiz

What does makemigrations do?

  • Applies changes to the database
  • Writes migration files describing model changes
  • Deletes old migrations
  • Runs the development server

Answer: Writes migration files describing model changes. makemigrations plans changes by writing migration files; it never touches the DB.

What does migrate do?

  • Applies pending migration files to the database
  • Generates migration files
  • Prints the SQL only
  • Creates a new project

Answer: Applies pending migration files to the database. migrate executes the migration files against the database.

A migration file's dependencies attribute points at what?

  • The database password
  • Migrations that must run first
  • Unrelated apps to ignore
  • The settings module

Answer: Migrations that must run first. dependencies list the migrations that must be applied before this one.

How does Django decide the real order to apply migrations?

  • Alphabetically by file name
  • Randomly
  • By a topological sort of the dependency graph
  • By file size

Answer: By a topological sort of the dependency graph. Django topologically sorts the migration graph using dependencies.

Which operation runs Python to transform existing rows during migrate?

  • AddField
  • CreateModel
  • RunPython
  • AlterField

Answer: RunPython. migrations.RunPython runs a function to move or transform data.

Inside a data migration, how should you access a model?

  • Import it directly from models.py
  • apps.get_model('app', 'Model')
  • Model.objects directly
  • From settings

Answer: apps.get_model('app', 'Model'). Use apps.get_model() to get the historical model version.

What does migrate --fake do?

  • Deletes the migration
  • Runs the SQL twice
  • Reverses all migrations
  • Marks a migration applied without running its SQL

Answer: Marks a migration applied without running its SQL. --fake records a migration as applied without executing it.

Which command prints the SQL a migration would run without applying it?

  • sqlmigrate
  • showmigrations
  • squashmigrations
  • dbshell

Answer: sqlmigrate. sqlmigrate prints the SQL for a migration without running it.

Should migration files be committed to version control?

  • No, they are temporary
  • Only on production
  • Yes, always
  • Only the initial one

Answer: Yes, always. Migrations are source code recording schema history and must be committed.

How do you resolve conflicting leaf migrations from two branches?

  • Delete one migration
  • Reinstall Django
  • Run migrate --fake
  • Run makemigrations --merge

Answer: Run makemigrations --merge. makemigrations --merge adds a migration depending on both leaves.