Topic 08 / 12

Migrations Demystified

~14 min read  //  Django Part 1 Series  //  Coding India

1. What is a Migration?

When you define a Django model, you have described what you want your database to look like. But the database itself has not changed yet. A migration is a Python file that contains the specific database instructions needed to make the actual database match your model definitions.

Think of migrations as a version control system specifically for your database schema. Each migration file records one set of changes (add a table, add a column, change a data type), and Django applies them in order to build up the complete schema over time.

2. The Two-Step Migration Process

Step 1: makemigrations — Generate the Migration File

python manage.py makemigrations

Django inspects all your models.py files, compares them to the last known state of the database, and generates a migration file describing the differences. After running it for the first time, you will see:

Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Post

Open that generated file to see what Django created:

# blog/migrations/0001_initial.py
from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('auth', '0012_alter_user_first_name_max_length'),
    ]

    operations = [
        migrations.CreateModel(
            name='Post',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True)),
                ('title', models.CharField(max_length=200)),
                ('slug', models.SlugField(max_length=200, unique=True)),
                ('body', models.TextField()),
                ('created_at', models.DateTimeField(auto_now_add=True)),
                ('updated_at', models.DateTimeField(auto_now=True)),
                ('published', models.BooleanField(default=False)),
                ('author', models.ForeignKey(
                    on_delete=django.db.models.deletion.CASCADE,
                    related_name='posts',
                    to='auth.user',
                )),
            ],
        ),
    ]

This is a plain Python file. Django uses the operations list to know exactly which SQL commands to execute.

Step 2: migrate — Apply the Migration to the Database

python manage.py migrate

This command reads all unapplied migration files (for your app and Django’s built-in apps like auth and sessions) and executes them against the database. You will see:

Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  ...
  Applying blog.0001_initial... OK

Under the hood, for our Post model, Django executes SQL equivalent to:

CREATE TABLE "blog_post" (
    "id"         bigserial PRIMARY KEY,
    "title"      varchar(200) NOT NULL,
    "slug"       varchar(200) NOT NULL UNIQUE,
    "body"       text NOT NULL,
    "created_at" timestamp with time zone NOT NULL,
    "updated_at" timestamp with time zone NOT NULL,
    "published"  boolean NOT NULL DEFAULT false,
    "author_id"  bigint NOT NULL REFERENCES "auth_user"("id") ON DELETE CASCADE
);

Django also creates a special table called django_migrations that records every migration that has been applied. This is how Django knows which migrations are pending and which are already done.

3. The Migration Workflow for Schema Changes

Every time you change a model (add a field, rename a field, delete a field), follow this same two-step process:

# 1. Add a field to your model in models.py
# 2. Generate a new migration
python manage.py makemigrations

# 3. Apply it to the database
python manage.py migrate

For example, adding a views_count field generates a second migration file:

blog/migrations/0002_post_views_count.py
    - Add field views_count to post

4. Never Edit Migration Files Manually

Migration files are auto-generated by Django. You should never edit them by hand unless you are an expert and have a very specific reason. If you make a mistake in your model, simply fix the model and run makemigrations again — Django will generate a new corrective migration.