Migrations
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
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 yetAfter adding the Course model and running makemigrations:
Migrations for 'courses':
courses/migrations/0001_initial.py
+ Create model CourseThen migrate:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, courses, sessions
Running migrations:
Applying courses.0001_initial... OKRun both every time you change a model: add a field, remove one, change a type, add a model.
A migration file
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:
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
python manage.py showmigrations coursescourses
[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:
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:
level = models.CharField(max_length=20, default="beginner")
# or
level = models.CharField(max_length=20, blank=True)Undoing and fixing
python manage.py migrate courses 0001 # roll back to migration 0001
python manage.py migrate courses zero # undo every migration of the app| Problem | Fix |
|---|---|
| "No changes detected" | the app isn't in INSTALLED_APPS, or the model file has an error |
| a migration conflicts with a teammate's | python manage.py makemigrations --merge |
| tables are missing on the server | migrate wasn't run as part of deployment |