Migrations Basics

Generate and apply schema changes with makemigrations and migrate. Learn migration files, dependencies, squashing, and safe workflows.

1. Introduction

Migrations are Django's way of tracking and applying changes to your database schema. Every time you add a field, rename a model, or change a relationship, Django records that change in a migration file and applies it to the database when you run migrate.

This guide covers the full migrations workflow — creating, applying, inspecting, and safely managing migrations in both development and production.

  • You should already have models defined in your app.
  • Your .venv must be active and Django 5.2 installed.

2. The two commands you use every day

# Step 1 — generate migration files from model changes
python manage.py makemigrations

# Step 2 — apply migration files to the database
python manage.py migrate

You run these two commands every time you change a model. They are always run in this order — makemigrations first, then migrate.

makemigrations does not touch the database. It only reads your models and writes Python files that describe the change. migrate reads those files and runs the actual SQL against the database.

3. What a migration file looks like

After running makemigrations, Django creates a file inside the app's migrations/ folder. For example, after defining the Article model:

pages/
└── migrations/
    ├── __init__.py
    └── 0001_initial.py

Open 0001_initial.py and you will see something like this:

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = []

    operations = [
        migrations.CreateModel(
            name='Article',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True)),
                ('title', models.CharField(max_length=200)),
                ('content', models.TextField()),
                ('created', models.DateTimeField(auto_now_add=True)),
                ('updated', models.DateTimeField(auto_now=True)),
            ],
        ),
    ]

Each migration file contains a Migration class with a list of operations. Django reads these operations and generates the SQL to run against the database. You rarely need to edit migration files by hand — but it helps to understand what is inside them.

4. Useful migration commands

Check migration status

python manage.py showmigrations

# Output example:
# pages
#  [X] 0001_initial        ← applied
#  [ ] 0002_article_slug   ← not yet applied

See the SQL without applying it

python manage.py sqlmigrate pages 0001
# Prints the raw SQL that this migration will run

Run migrations for a specific app only

python manage.py migrate pages

Roll back to a previous migration

# Roll back to just after 0001 was applied (undoes 0002 and later)
python manage.py migrate pages 0001

Check for issues without applying

python manage.py migrate --check
# Exits with a non-zero code if there are unapplied migrations

5. Making model changes safely

Every time you change a model, follow this workflow:

  1. Make your change in models.py.
  2. Run python manage.py makemigrations to generate the migration.
  3. Review the generated migration file to confirm it matches what you intended.
  4. Run python manage.py migrate to apply it.
  5. Commit both models.py and the new migration file to Git together.

6. Migration dependencies

Each migration declares which migrations it depends on. This creates a chain — Django applies them in the correct order regardless of the file names.

class Migration(migrations.Migration):

    dependencies = [
        ('pages', '0001_initial'),
    ]

When migrations span multiple apps — for example adding a ForeignKey to a model in another app — Django adds that app's migration to the dependencies automatically. You will see something like ('auth', '0001_initial') in the dependencies list.

7. Squashing migrations

Over time your app can accumulate dozens of migration files. Squashing merges a range of migrations into a single file, which speeds up fresh installations and makes the migration history easier to follow.

# Squash migrations 0001 through 0010 into one file
python manage.py squashmigrations pages 0001 0010

Django generates a new migration file that replaces the squashed range. Old migration files are kept until all environments have applied the squashed migration, then they can be safely deleted.

8. Common mistakes

Forgetting to run makemigrations after a model change

The database stays out of sync with your models. Run python manage.py check to catch this early — it warns when models and migrations are out of sync.

Deleting migration files that have already been applied

Django tracks which migrations have been applied in a django_migrations table. Deleting applied migration files confuses Django and breaks the chain. Never delete applied migrations — squash them instead.

Editing a migration after it has been applied

Once a migration is applied in any environment, treat it as read-only. Create a new migration for any further changes instead of editing the existing one.

9. Next steps

You now understand how migrations work and how to manage them safely. The final page in this category covers data migrations — how to write migrations that transform or backfill existing data using RunPython.


Never miss a story on Django.wiki

Subscribe for fresh tutorials, snippets, and updates.

By subscribing you agree to our Privacy Policy.