Django migrations
Prerequisites¶
Django Migrations¶
Migrations are Django's way of propagating changes made to your models (defined in models.py
) into your database schema. They allow you to:
- Create database schemas from your models.
- Modify existing schemas when your models change.
- Share changes between team members or deployments.
Migrations are stored as files inside the migrations
directory of each Django app and contain Python code to apply (or undo) specific changes to the database.
The Migration Workflow¶
-
Make Changes to Models
Update or define new models inmodels.py
. -
Generate Migration File
Run themakemigrations
command to create a migration file based on the changes made to your models:python manage.py makemigrations
-
Apply Migrations
Apply the migration file to the database using:python manage.py migrate
-
Inspect Migration Status
Check the status of migrations with:python manage.py showmigrations
Example: Step-by-Step Migration Process¶
1. Creating an Initial Migration¶
Suppose we have a simple app called blog
. In models.py
:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_date = models.DateTimeField()
Run the following command to create an initial migration:
python manage.py makemigrations
This generates a migration file in blog/migrations/
, typically named something like 0001_initial.py
.
The migration file might look like:
# Generated by Django 5.x on YYYY-MM-DD HH:MM
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='Post',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('content', models.TextField()),
('published_date', models.DateTimeField()),
],
),
]
Apply the migration:
python manage.py migrate
This creates the Post
table in the database.
2. Adding a New Field¶
Now, let’s add an author
field to the Post
model:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_date = models.DateTimeField()
author = models.CharField(max_length=100) # New field
Run makemigrations
again:
python manage.py makemigrations
A new migration file will be generated, e.g., 0002_add_author_field.py
:
# Generated by Django 5.x on YYYY-MM-DD HH:MM
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='post',
name='author',
field=models.CharField(default='', max_length=100),
),
]
Apply the migration:
python manage.py migrate
3. Removing a Field¶
If we decide to remove the author
field:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_date = models.DateTimeField()
# author field removed
Run makemigrations
:
python manage.py makemigrations
This generates a new migration file, e.g., 0003_remove_author_field.py
:
# Generated by Django 5.x on YYYY-MM-DD HH:MM
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_add_author_field'),
]
operations = [
migrations.RemoveField(
model_name='post',
name='author',
),
]
Apply the migration:
python manage.py migrate
Advanced Topics¶
1. Customizing Migration Operations¶
You can define custom SQL or Python logic in migrations using RunSQL
or RunPython
.
For example, adding default data:
from django.db import migrations
def add_default_posts(apps, schema_editor):
Post = apps.get_model('blog', 'Post')
Post.objects.create(title='Hello World', content='Welcome to the blog!', published_date='2024-01-01')
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
]
operations = [
migrations.RunPython(add_default_posts),
]
2. Rolling Back Migrations¶
You can reverse migrations using:
python manage.py migrate app_name migration_name
python manage.py migrate blog 0001
0001
. 3. Squashing Migrations¶
As projects grow, the number of migrations can become unwieldy. Squashing combines multiple migrations into one:
python manage.py squashmigrations blog 0001 0003
Common Issues and Solutions¶
1. Migration Conflicts¶
When multiple developers create migrations independently, conflicts can occur. Resolve them by editing dependencies or merging operations manually.
2. Missing Migrations¶
If Django detects model changes not reflected in migrations, ensure to run makemigrations
and apply them.
3. Data Loss¶
Be cautious with destructive operations like RemoveField
. Test migrations in a staging environment before applying to production.
Best Practices¶
- Use descriptive names for fields and migrations.
- Test migrations locally or in staging before applying to production.
- Avoid editing migration files directly unless necessary.
- Keep migration files under version control.