0
0
DjangoHow-ToBeginner · 4 min read

How to Create Custom Migration in Django: Step-by-Step Guide

To create a custom migration in Django, generate a migration file using python manage.py makemigrations and then edit it to add a RunPython operation with your custom Python function. This lets you run specific code during migration, such as data transformations or complex schema changes.
📐

Syntax

A custom migration in Django uses the RunPython operation inside a migration file. You define two functions: one for applying the migration and one for reversing it. These functions receive apps and schema_editor as parameters to access models and the database.

  • RunPython(forwards_func, backwards_func): Runs Python code during migration.
  • forwards_func(apps, schema_editor): Code to apply changes.
  • backwards_func(apps, schema_editor): Code to undo changes.
python
from django.db import migrations

def forwards_func(apps, schema_editor):
    # Your code here
    pass

def backwards_func(apps, schema_editor):
    # Undo code here
    pass

class Migration(migrations.Migration):
    dependencies = [
        # ('app_name', 'previous_migration'),
    ]

    operations = [
        migrations.RunPython(forwards_func, backwards_func),
    ]
💻

Example

This example shows a custom migration that sets a default value for a new field is_active in a model UserProfile. The forwards_func sets is_active to True for all existing records. The backwards_func resets it to False.

python
from django.db import migrations

def forwards_func(apps, schema_editor):
    UserProfile = apps.get_model('myapp', 'UserProfile')
    for user in UserProfile.objects.all():
        user.is_active = True
        user.save()

def backwards_func(apps, schema_editor):
    UserProfile = apps.get_model('myapp', 'UserProfile')
    for user in UserProfile.objects.all():
        user.is_active = False
        user.save()

class Migration(migrations.Migration):
    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(forwards_func, backwards_func),
    ]
Output
When applied, all UserProfile records will have is_active set to True; when reversed, set to False.
⚠️

Common Pitfalls

  • Not defining a reverse function can make rollback impossible; always provide a backwards_func or use migrations.RunPython.noop.
  • Accessing models directly from imports instead of using apps.get_model can cause errors because migrations use historical models.
  • Performing heavy database operations inside migrations can slow down deployment; keep logic simple.
python
from django.db import migrations

# Wrong: importing model directly
#from myapp.models import UserProfile

def forwards_func(apps, schema_editor):
    UserProfile = apps.get_model('myapp', 'UserProfile')  # Correct way
    # ... your code ...

class Migration(migrations.Migration):
    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(forwards_func),  # No reverse function - risky
    ]
📊

Quick Reference

Remember these key points when creating custom migrations:

  • Use RunPython to run Python code during migrations.
  • Always use apps.get_model to access models inside migration functions.
  • Provide both forward and backward functions for safe rollbacks.
  • Keep migration code simple and fast.

Key Takeaways

Use migrations.RunPython with forward and backward functions to create custom migrations.
Access models inside migrations with apps.get_model, not direct imports.
Always provide a reverse function to allow safe rollback of migrations.
Keep migration logic simple to avoid slow deployments.
Test custom migrations in a development environment before applying to production.