CodeMIRAI
0 XP

Migrations

2 min read 1 runnable example Quiz included

Models describe what the database should look like. Migrations are the steps that get it there, recorded as files, so every copy of the project (yours, a teammate's, the live server) ends up with exactly the same tables.

The two commands

Terminal
python manage.py makemigrations    # 1. compare models with the last migration, write a new one
python manage.py migrate           # 2. apply any migrations the database hasn't had yet

After adding the Course model and running makemigrations:

Output
Migrations for 'courses':
  courses/migrations/0001_initial.py
    + Create model Course

Then migrate:

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

Run both every time you change a model: add a field, remove one, change a type, add a model.

A migration file

Python
from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True
    dependencies = []

    operations = [
        migrations.CreateModel(
            name="Course",
            fields=[
                ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("title", models.CharField(max_length=100)),
                ("price", models.DecimalField(decimal_places=2, default=0, max_digits=8)),
            ],
        ),
    ]

Migration files are ordinary Python, generated for you. Commit them to version control with the rest of your code: they're the history of your database's structure.

The SQL behind a model

Django writes the SQL for you. This runs here, and shows the SQL that creates the Course table, collected instead of executed:

Python
import django
from django.conf import settings

settings.configure(
    INSTALLED_APPS=["django.contrib.contenttypes"],
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
)
django.setup()

from django.db import connection, models


class Course(models.Model):
    title = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=8, decimal_places=2, default=0)

    class Meta:
        app_label = "courses"


with connection.schema_editor(collect_sql=True) as editor:
    editor.create_model(Course)
for statement in editor.collected_sql:
    print(statement)

In a project, python manage.py sqlmigrate courses 0001 prints the same kind of SQL for a migration file. The exact SQL depends on the database: this is SQLite's version, and PostgreSQL's or MySQL's would differ slightly.

Seeing what's applied

Terminal
python manage.py showmigrations courses
Output
courses
 [X] 0001_initial
 [X] 0002_course_published
 [ ] 0003_lesson

[X] means applied, and [ ] means waiting for migrate.

Adding a field to a table that has rows

Suppose Course gains a new field, and the table already has courses in it:

Python
level = models.CharField(max_length=20)

makemigrations stops and asks what existing rows should contain, because the new column can't be empty. Avoid the question by giving the field a default, or by allowing empty values:

Python
level = models.CharField(max_length=20, default="beginner")
# or
level = models.CharField(max_length=20, blank=True)

Undoing and fixing

Terminal
python manage.py migrate courses 0001     # roll back to migration 0001
python manage.py migrate courses zero     # undo every migration of the app
ProblemFix
"No changes detected"the app isn't in INSTALLED_APPS, or the model file has an error
a migration conflicts with a teammate'spython manage.py makemigrations --merge
tables are missing on the servermigrate wasn't run as part of deployment
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.