Migrations

Difficulty: Beginner

Migrations are Django's way of propagating changes you make to your models (adding a field, deleting a model, etc.) into the database schema. They are designed to be mostly automatic: Django can detect model changes and generate migration files, and then apply those files to update the database.

The migration workflow involves two commands. 'python manage.py makemigrations' examines your models, compares them to the current migration files, and generates new migration files for any changes detected. 'python manage.py migrate' applies pending migration files to the database, executing the necessary SQL statements.

Migration files are Python scripts stored in each app's migrations/ directory. They are named with a sequential number prefix (0001_initial.py, 0002_add_slug_field.py, etc.) and contain a list of operations that describe the changes. Operations include CreateModel, AddField, RemoveField, AlterField, RenameField, RenameModel, and more. Each migration file also records its dependencies - which migrations must be applied before it.

Django tracks which migrations have been applied in a special database table called django_migrations. When you run 'migrate', Django checks this table to determine which migrations are pending and applies only those. This is why you should never manually modify or delete migration files after they have been applied to a shared database - it can cause the migration state to become inconsistent.

The 'showmigrations' command displays the status of all migrations. Each migration is listed with an [X] if applied or [ ] if pending. This is useful for debugging migration issues. The 'sqlmigrate' command shows the raw SQL that a migration will execute, which is helpful for understanding what Django will do to your database.

Data migrations are a special type of migration that modifies data rather than schema. You create them with 'python manage.py makemigrations --empty appname' and then write Python code to transform existing data. This is useful when you need to backfill a new field, convert data formats, or merge records.

Squashing migrations reduces the number of migration files by combining multiple sequential migrations into one. Run 'python manage.py squashmigrations appname 0001 0010' to squash migrations 0001 through 0010. This improves performance for large projects with many migrations and makes the migration history cleaner.

In team environments, migration conflicts can occur when two developers create migrations independently. Django detects this and will prompt you to resolve the conflict, usually by running 'makemigrations --merge' which creates a merge migration that depends on both conflicting migrations.

Code examples

Basic Migration Workflow

# 1. Make changes to your model
# models.py
class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    # NEW: added slug field
    slug = models.SlugField(unique=True, default='')

# 2. Generate migration file
# $ python manage.py makemigrations
# Migrations for 'blog':
#   blog/migrations/0002_article_slug.py
#     - Add field slug to article

# 3. Review the migration SQL (optional)
# $ python manage.py sqlmigrate blog 0002
# ALTER TABLE "blog_article" ADD COLUMN "slug" varchar(50) ...

# 4. Apply the migration
# $ python manage.py migrate
# Applying blog.0002_article_slug... OK

# 5. Check migration status
# $ python manage.py showmigrations blog
# blog
#  [X] 0001_initial
#  [X] 0002_article_slug

This shows the standard workflow: change your model, generate a migration with makemigrations, optionally review the SQL with sqlmigrate, apply with migrate, and verify with showmigrations.

Migration File Anatomy

# blog/migrations/0002_article_slug.py
from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('blog', '0001_initial'),  # Depends on previous migration
    ]

    operations = [
        migrations.AddField(
            model_name='article',
            name='slug',
            field=models.SlugField(default='', unique=True),
        ),
    ]

Migration files are Python classes with two key attributes: dependencies (which migrations must run first) and operations (what changes to make). Django generates these automatically - you rarely need to edit them manually.

Data Migration

# Create an empty migration:
# $ python manage.py makemigrations blog --empty -n populate_slugs

# blog/migrations/0003_populate_slugs.py
from django.db import migrations
from django.utils.text import slugify

def populate_slugs(apps, schema_editor):
    Article = apps.get_model('blog', 'Article')
    for article in Article.objects.all():
        article.slug = slugify(article.title)
        article.save(update_fields=['slug'])

def reverse_slugs(apps, schema_editor):
    Article = apps.get_model('blog', 'Article')
    Article.objects.all().update(slug='')

class Migration(migrations.Migration):
    dependencies = [
        ('blog', '0002_article_slug'),
    ]

    operations = [
        migrations.RunPython(populate_slugs, reverse_slugs),
    ]

Data migrations use RunPython to execute custom Python code. Always provide a reverse function so the migration can be reversed. Use apps.get_model() to access models (not direct imports) because the model state may differ from the current code.

Useful Migration Commands

# Generate migrations for all apps
python manage.py makemigrations

# Generate migration for a specific app
python manage.py makemigrations blog

# Apply all pending migrations
python manage.py migrate

# Apply migrations for a specific app
python manage.py migrate blog

# Roll back to a specific migration
python manage.py migrate blog 0001

# Roll back all migrations for an app
python manage.py migrate blog zero

# Show migration status
python manage.py showmigrations

# Show SQL for a migration
python manage.py sqlmigrate blog 0002

# Merge conflicting migrations
python manage.py makemigrations --merge

These are the essential migration commands. Rolling back with 'migrate blog 0001' unapplies all migrations after 0001. 'migrate blog zero' unapplies all migrations for the app. --merge resolves branch conflicts.

Key points

Concepts covered

makemigrations, migrate, Migration Files, Schema Changes, Data Migrations