Django cookbook · Intermediate
Write a reversible data migration
A new column needs values computed from existing data, deployed safely with the schema change.
Approach
Use RunPython with historical models from apps.get_model (never import current models), process rows in
batches, and provide a reverse function so the migration can be rolled back.
Solution
# shop/migrations/0012_backfill_slug.py
from django.db import migrations
from django.utils.text import slugify
def forwards(apps, schema_editor):
Product = apps.get_model("shop", "Product")
batch = []
for product in Product.objects.filter(slug="").only("id", "name").iterator(chunk_size=1000):
product.slug = slugify(product.name)[:50] or f"product-{product.id}"
batch.append(product)
if len(batch) >= 1000:
Product.objects.bulk_update(batch, ["slug"])
batch.clear()
Product.objects.bulk_update(batch, ["slug"])
def backwards(apps, schema_editor):
apps.get_model("shop", "Product").objects.update(slug="")
class Migration(migrations.Migration):
dependencies = [("shop", "0011_product_slug")]
operations = [migrations.RunPython(forwards, backwards)]
Pitfalls
- Importing
from shop.models import Productin a migration; it breaks when the model changes later. - Loading every row into memory with
.all()on large tables. - Combining a long data migration with schema changes on databases without transactional DDL.
Variations
- For very large tables, backfill with a management command in batches after deploying, then add the constraint.
- Use
migrations.RunPython.noopas the reverse when there's nothing to undo.
Further reading: Data migrations
Reference: https://docs.djangoproject.com/en/6.0/topics/migrations/#data-migrations
More Django recipes
- Add a required column without downtime
- Add full-text search with PostgreSQL
- Add health checks and structured logging
- Avoid N+1 queries with select_related and prefetch_related
- Cache expensive queries and invalidate on change
- Call external APIs concurrently in async views
- Configure settings from environment variables
- Enforce rules with database constraints