from django.db import migrations
def populate_slugs(apps, schema_editor):
"""Generate slugs for existing posts."""
Post = apps.get_model('blog', 'Post')
from django.utils.text import slugify
for post in Post.objects.filter(slug__isnull=True):
post.slug = slugify(post.title)
post.save()
def reverse_populate_slugs(apps, schema_editor):
"""Clear slugs."""
Post = apps.get_model('blog', 'Post')
Post.objects.update(slug=None)
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_post_slug'),
]
operations = [
migrations.RunPython(populate_slugs, reverse_populate_slugs),
]
Migrations track database schema changes. I use makemigrations after model changes and review generated migrations. For data migrations, I create empty migrations with makemigrations --empty and write RunPython operations. I test migrations on dev data before production. For reversibility, I implement reverse() functions. I squash old migrations periodically to reduce count. In team environments, I coordinate migrations to avoid conflicts. For zero-downtime deployments, I split destructive changes across releases. This keeps database schema in sync with code safely.