Migrations
🔹 1. Understanding Migrations
Migrations are: - Auto-generated files that store changes to your models. - Applied to the database to create/update/delete tables and fields. - Version-controlled so changes can be tracked over time.
They ensure consistency between the database and Django models.
🔹 2. Key Migration Commands
Command | Description |
---|---|
python manage.py makemigrations |
Generates migration files based on model changes. |
python manage.py migrate |
Applies migrations to the database. |
python manage.py showmigrations |
Displays a list of applied and unapplied migrations. |
python manage.py sqlmigrate <app_name> <migration_number> |
Shows the raw SQL that will be executed for a migration. |
python manage.py migrate <app_name> zero |
Rolls back all migrations for an app. |
python manage.py makemigrations --dry-run |
Shows what migrations will be created without making changes. |
python manage.py migrate --fake-initial |
Marks initial migrations as applied without actually running them. |
🔹 3. How Migrations Work
Step 1: Create a Model
from django.db import models
class Product(models.Model):
= models.CharField(max_length=255)
name = models.DecimalField(max_digits=10, decimal_places=2)
price = models.DateTimeField(auto_now_add=True) created_at
Run:
python manage.py makemigrations
This creates a migration file, e.g., 0001_initial.py
, inside migrations/
.
Step 2: Apply the Migration
python manage.py migrate
✅ Django creates the corresponding SQL table.
To see the SQL executed:
python manage.py sqlmigrate <app_name> 0001
🔹 4. Common Migration Operations
🛠 Adding a New Field
Add a category
field to the Product
model:
= models.CharField(max_length=100, default="General") category
Run:
python manage.py makemigrations
python manage.py migrate
✅ The field is added without losing existing data.
🛠 Renaming a Field
Change name
to title
:
class Product(models.Model):
= models.CharField(max_length=255) # Renamed title
Django automatically detects this change and creates a migration.
🛠 Removing a Field
Delete the category
field:
class Product(models.Model):
= models.CharField(max_length=255) title
Run:
python manage.py makemigrations
python manage.py migrate
✅ Django removes the column from the database.
🛠 Changing Field Type
Change price
from DecimalField
to IntegerField
:
= models.IntegerField() price
Django will warn if data conversion might cause data loss.
🛠 Creating a New Model
Add an Order
model:
class Order(models.Model):
= models.ForeignKey(Product, on_delete=models.CASCADE)
product = models.IntegerField()
quantity = models.DateTimeField(auto_now_add=True) order_date
Run:
python manage.py makemigrations
python manage.py migrate
✅ Django creates a new table in the database.
🔹 5. Rolling Back Migrations
Undo the Last Migration
python manage.py migrate <app_name> <previous_migration_number>
🔹 Example: If the latest migration is 0003_auto
, roll back to 0002
:
python manage.py migrate product 0002
Undo All Migrations for an App
python manage.py migrate <app_name> zero
🔹 Example:
python manage.py migrate product zero
✅ This removes all tables for product
but does not delete migration files.
🔹 6. Handling Migration Issues
🛑 Missing Migrations Error
If Django detects model changes but no migrations were created, run:
python manage.py makemigrations
python manage.py migrate
🛑 “Table already exists” Error
This happens when Django tries to create a table that already exists.
🔹 Fix: Fake the initial migration:
python manage.py migrate <app_name> --fake-initial
🛑 Foreign Key Constraint Error
This happens if a related model does not exist.
🔹 Fix: Ensure the related model has been migrated first:
python manage.py migrate <related_app>
🛑 Reverse a Corrupt Migration
If a migration was applied incorrectly:
python manage.py migrate <app_name> <previous_migration>
🔹 Example:
python manage.py migrate product 0001
Then delete the incorrect migration file and re-run:
python manage.py makemigrations
python manage.py migrate
🔹 7. Deleting Migrations and Starting Fresh
⚠️ This is destructive and removes all migration history.
Step 1: Delete Migration Files
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
Step 2: Reset the Database
python manage.py flush
This deletes all database records but keeps tables.
Step 3: Recreate Migrations
python manage.py makemigrations
python manage.py migrate
✅ Now your project has a clean migration history.
🔹 8. Migrating Models to Another App (Without Data Loss)
If you move a model from app_a
to app_b
, follow these steps:
Step 1: Add db_table
in the New Model
class MyModel(models.Model):
class Meta:
= "app_a_mymodel" db_table
Step 2: Fake the Migration
Run:
python manage.py makemigrations app_b
python manage.py migrate app_b --fake-initial
✅ The database remains unchanged, but Django recognizes the model in the new app.
🔹 9. Best Practices
✅ Always Commit Migration Files – Never ignore migrations in version control.
✅ Use --dry-run
Before Applying Changes – Run: bash python manage.py makemigrations --dry-run
✅ Keep Migrations Linear – Don’t edit old migration files unless necessary.
✅ Use --fake-initial
When Moving Models – Prevents Django from recreating tables.
✅ Backup Before Major Changes – Always use: bash python manage.py dumpdata > backup.json
✅ 10. Basic Custom Migration (Add a Data Change)
Let’s say you want to insert default categories into your Category
model.
Step 1: Create a blank migration
python manage.py makemigrations --empty your_app_name
This generates something like:
your_app/migrations/0002_auto_add_default_categories.py
Step 2: Edit the migration file
# your_app/migrations/0002_auto_add_default_categories.py
from django.db import migrations
def add_default_categories(apps, schema_editor):
= apps.get_model('your_app', 'Category')
Category
Category.objects.bulk_create([="Food"),
Category(name="Transport"),
Category(name="Utilities"),
Category(name
])
class Migration(migrations.Migration):
= [
dependencies 'your_app', '0001_initial'), # Update based on your actual last migration
(
]
= [
operations
migrations.RunPython(add_default_categories), ]
✅ 11. Add Reverse Function (for Rollbacks)
For full reversibility:
def remove_default_categories(apps, schema_editor):
= apps.get_model('your_app', 'Category')
Category filter(name__in=["Food", "Transport", "Utilities"]).delete()
Category.objects.
= [
operations =remove_default_categories),
migrations.RunPython(add_default_categories, reverse_code ]
If no reverse logic needed, you can use
migrations.RunPython(add_default_categories, reverse_code=migrations.RunPython.noop)
✅ 12. Custom Schema Changes (Manual SQL or Conditional Alter)
For raw SQL:
"ALTER TABLE my_table ADD COLUMN custom_field INT DEFAULT 0") migrations.RunSQL(
For conditional logic:
def alter_schema_if_condition(apps, schema_editor):
if schema_editor.connection.vendor == 'postgresql':
"ALTER TABLE ...") schema_editor.execute(
✅ 13. Custom Migration for Complex Logic (e.g., data transformation)
You can use the apps.get_model()
trick to avoid direct model import:
def upgrade_data_format(apps, schema_editor):
= apps.get_model('your_app', 'Transaction')
Transaction for txn in Transaction.objects.all():
= txn.description.upper()
txn.description txn.save()
⚠️ Avoid importing real models from your app in migrations — always use apps.get_model()
to get historical versions of models.
✅ Migration Lifecycle Recap
# Create an empty migration
python manage.py makemigrations --empty your_app
# Apply it
python manage.py migrate
# View history
python manage.py showmigrations
# Fake apply (if you already did it manually)
python manage.py migrate your_app_name --fake
🎯 Summary of Key Migration Commands
Command | Action |
---|---|
makemigrations |
Create migration files |
migrate |
Apply migrations to the database |
showmigrations |
List applied and unapplied migrations |
sqlmigrate <app> <num> |
Show SQL for a migration |
migrate <app> zero |
Roll back all migrations for an app |
migrate --fake-initial |
Mark migrations as applied without running them |
flush |
Reset database (delete all data) |
🧪 Best Practices
Tip | Why It Helps |
---|---|
Use apps.get_model() |
Avoid importing future-incompatible models |
Add reverse migrations | Enables rollback |
Keep business logic out | Keep migrations lean, call helpers if needed |
Document purpose in name | Use --name when generating migration |
Avoid .save() in loops |
Use bulk_create() or update() where possible |