Migrations: turning models into database tables
The Post model from part 5 is just a Python class until a migration turns it into an actual database table. Migrations are Django's version-controlled history of every schema change a project has ever made — generated from your models, applied to the database, and committed to git like any other code.
Generating a migration
python manage.py makemigrations blogMigrations for 'blog':
blog/migrations/0001_initial.py
- Create model Postmakemigrations compares the current state of models.py against the last migration and writes a new file describing the difference — it doesn't touch the database at all yet. Run it every time a model changes: add a field, remove one, rename one, each gets its own migration.
Reading the migration file
# blog/migrations/0001_initial.py
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Post",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
("title", models.CharField(max_length=200)),
("slug", models.SlugField(max_length=200, unique=True)),
("content", models.TextField()),
("published", models.BooleanField(default=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("author", models.ForeignKey(
on_delete=models.deletion.CASCADE, to=settings.AUTH_USER_MODEL,
)),
],
options={"ordering": ["-created_at"]},
),
]Every field from Post in part 5 has a corresponding entry here, and dependencies records that this migration needs the user model's migration applied first — since author is a ForeignKey to it. This file is meant to be read, not just generated and forgotten: it's the actual record of what the database looked like at this point in the project's history.
Applying the migration
python manage.py migrateOperations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OKmigrate is the step that actually touches the database — it runs every unapplied migration, in dependency order, including the built-in ones from admin/auth/sessions that were waiting since part 1. By default Django projects use SQLite (a single db.sqlite3 file, zero setup), which is exactly right for development and this series; a real deployment (part 22) typically switches to PostgreSQL.
Editing a migration file by hand instead of changing the model and running makemigrations again. The migration is generated output — if it's wrong, the fix is almost always in models.py, then regenerate.
The two-step habit
makemigrations then migrate — always both, always in that order, every time a model changes. Skipping migrate after makemigrations is the single most common way to see "no such column" errors on a model that looks completely correct in models.py.
Next: the Django admin — a full interface for managing this data, with zero HTML written by hand.