← all lessons

dailyfeed · 2026-04-27 · djangomigrationsschema

Django migrations

The idea

A migration is a small Python file that describes a change to the database schema — "add this column," "drop this index," "rename this table." Migrations are committed source code: anyone running python manage.py migrate replays the migrations they haven't applied yet, in order, and ends up with the same schema as you.

Two commands matter:

The mental model: your models.py is the desired state; the migration files are the change history that gets the database from one state to the next. Django tracks which migrations have run in the django_migrations table.

The most common Django footgun: editing models.py and forgetting to run makemigrations. The code looks right, the model is "updated," but the database has no idea. Tests using --keepdb will mask it.

How it shows up

This PR added four fields to UserFeed. Running makemigrations produced 0014_userfeed_scheduling_fields.py, which contains a list of migrations.AddField(...) operations. That file is committed alongside the model change — they always travel together.

operations = [
    migrations.AddField(
        model_name="userfeed",
        name="next_check_at",
        field=models.DateTimeField(blank=True, db_index=True, null=True),
    ),
    ...
]

Read more

Exercises

  1. Read a migration end-to-end — open 0014_userfeed_scheduling_fields.py and match each AddField to the corresponding line in models.py. Note that default="" shows up in the operation but db_index=True becomes its own CREATE INDEX step. Done when: you can describe what SQL Django will run for each operation.
  2. Make + unmake — add a throwaway field to a model, run makemigrations, look at the file, then delete the field, delete the migration file, and re-run makemigrations to see Django produce nothing. (Don't migrate it.) Get a feel for the diff-based generation. Done when: you've watched the migration file appear and disappear in response to model changes.
  3. Read RunPython — find a migration in this repo or another Django project that uses migrations.RunPython. That's how data migrations (not just schema) are expressed. Done when: you can name the difference between a schema migration (AddField, AlterField) and a data migration (RunPython, RunSQL).