What Migrations Do
Migrations are Python files that describe incremental changes to your database schema — adding a table, adding a column, altering a field's type — generated by running python manage.py makemigrations after changing models.py. Each migration file lists dependencies on prior migrations, forming a directed graph that Django's migration executor walks in order when you run python manage.py migrate. Migrations are database-agnostic: the same migration file can generate PostgreSQL, MySQL, or SQLite DDL depending on your configured backend.
Cricket analogy: A chain of migrations dependent on prior ones is like a Test match's over-by-over scorecard — over 24 can only be recorded after over 23 is complete, just as migration 0005 depends on 0004 being applied first.
makemigrations, migrate, and the Migration State
makemigrations only writes migration files to disk based on differences it detects between your models and the last known migration state — it does not touch the database. migrate is the command that actually applies (or reverses) those files against a real database, and Django tracks which migrations have run per app in the django_migrations table. Running python manage.py sqlmigrate app_name 0003 prints the raw SQL a migration will execute without running it, which is useful for reviewing risky schema changes like column drops before they hit production.
Cricket analogy: makemigrations writing a plan without touching the database is like a captain announcing a bowling change in the huddle — the decision is recorded, but no ball has been bowled (no SQL executed) until migrate runs it.
# After changing models.py
python manage.py makemigrations blog
# Inspect the SQL before running it against a real database
python manage.py sqlmigrate blog 0004
# Apply all pending migrations
python manage.py migrate
# Roll back to a specific earlier migration
python manage.py migrate blog 0002Editing a migration file after it has already been applied and shared with teammates (or deployed) is dangerous — Django tracks migrations by name and file state, so out-of-sync migration histories across environments can cause 'InconsistentMigrationHistory' errors or silently diverging schemas.
Data Migrations and Backward Compatibility
Beyond schema changes, migrations can also transform data using RunPython, letting you write a forward function (and optionally a reverse function) that operates on historical model state accessed via apps.get_model() rather than importing the model directly — this matters because the model as it exists today may have fields that didn't exist when an old migration ran. A common safe pattern for adding a required field to a table with existing rows is a three-step migration: first add the field as nullable, then run a data migration to backfill values, then a final migration to make the field non-nullable.
Cricket analogy: Using apps.get_model() instead of importing the live model in a data migration is like consulting the exact rulebook edition in force during a 2005 match instead of today's DRS-updated rules — you must reason about the schema as it existed at that point in history.
- makemigrations generates migration files from model changes but does not touch the database.
- migrate applies (or reverses) pending migration files against the actual database and records progress in django_migrations.
- sqlmigrate prints the raw SQL a migration will run, useful for reviewing risky changes before deployment.
- Migrations form a dependency graph; Django applies them in the order their dependencies require.
- RunPython enables data migrations, using apps.get_model() to access historical model state rather than the live model.
- Adding a required field to a table with existing data safely follows a three-step pattern: nullable, backfill, then non-nullable.
- Editing already-applied and shared migration files risks InconsistentMigrationHistory errors across environments.
Practice what you learned
1. What is the difference between makemigrations and migrate?
2. What does python manage.py sqlmigrate app_name 0003 do?
3. Why do data migrations use apps.get_model() instead of importing the model directly?
4. What is the safe three-step pattern for adding a required field to a table with existing rows?
5. Where does Django track which migrations have already been applied to a given database?
Was this page helpful?
You May Also Like
Defining Models
How to declare Django models as Python classes that map to database tables, including field types, options, and Meta configuration.
Model Validation
How Django validates model data using field validators, clean methods, and full_clean(), and why validation isn't automatic on save().
Django ORM QuerySets
How QuerySets let you retrieve, filter, and chain database queries lazily using Python instead of raw SQL.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics