How to Safely Rename a Django Model or Database Field
A practical, step-by-step guide to renaming Django models and fields without losing data or breaking production — covering RenameModel, RenameField, ForeignKeys, and zero-downtime strategies.
On this page
- Why Renaming Is Risky
- Part 1: Renaming a Field
- Step 1 — Rename the field in models.py
- Step 2 — Run makemigrations interactively
- Step 3 — Inspect the generated migration
- Step 4 — Apply the migration
- Part 2: What If You Miss the Rename Prompt?
- Part 3: Renaming a Model
- Step 1 — Rename the model class
- Step 2 — Run makemigrations
- Step 3 — Verify the migration
- Step 4 — Apply the migration
- Keeping the Existing Database Table Name
- Part 4: Renaming a Model with ForeignKeys
- Don't Forget Python References
- Part 5: Zero-Downtime Renames in Production
- Expand/Contract Field Rename
- Deploy 1 — Expand
- Deploy 2 — Backfill Existing Data
- Deploy 3 — Dual-Write
- Deploy 4 — Switch Reads
- Deploy 5 — Contract
- Why Expand/Contract Works
- Common Pitfalls
- 1. Don't blindly run migrate
- 2. Be Careful with --noinput
- 3. Search for Every Code Reference
- 4. Test With Realistic Data
- 5. Think About Large Tables
- 6. Back Up Before Risky Schema Changes
- A Practical Rename Checklist
- Key Takeaways
- Do that consistently, and database renames become a controlled engineering task instead of a production gamble.
- Final Thoughts
- A migration file is not just a file Django generates for you. It's part of your application's database code. Review it like you would review any other important piece of code.
title: "How to Safely Rename a Django Model or Database Field" description: "Learn how to safely rename Django models and database fields without losing production data, including zero-downtime expand and contract migrations." author: "Your Name" publishedAt: "2026-09-15" tags:
- Django
- Python
- Database
- Migrations
- PostgreSQL
How to Safely Rename a Django Model or Database Field#
Renaming looks trivial — you change a name in models.py, run makemigrations, and you're done. Right?
Not quite.
Renaming is one of the operations that can become dangerous when working with a production database. Django needs to determine whether you've renamed an existing field or deleted one field and created another.
If Django generates the wrong migration and you apply it to production, you could lose an entire column of data.
This guide explains how to safely rename Django models and fields, from simple development changes to zero-downtime renames in production.
Why Renaming Is Risky#
Suppose you have a field called pub_date and want to rename it to published_date.
From your perspective, this is clearly a rename:
pub_date → published_date
But Django's migration autodetector sees two separate changes:
- A field called
pub_datedisappeared. - A field called
published_dateappeared.
Those changes could mean either:
Rename pub_date → published_date
or:
Delete pub_date
Create published_date
If Django generates the second interpretation, the migration might look like this:
migrations.RemoveField(
model_name="book",
name="pub_date",
),
migrations.AddField(
model_name="book",
name="published_date",
field=models.DateField(),
),
RemoveField removes the existing database column and its data. AddField then creates a new column.
Your old data is gone.
The good news is that Django can ask you to confirm whether the change is actually a rename.
The important part is to pay attention to that prompt and inspect the generated migration before applying it.
Part 1: Renaming a Field#
Let's start with the simplest example.
Suppose your model currently looks like this:
class Book(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateField()
You want to rename pub_date to published_date.
Step 1 — Rename the field in models.py#
Change the model to:
class Book(models.Model):
title = models.CharField(max_length=200)
published_date = models.DateField()
At this point, don't run migrate yet.
First, generate the migration.
Step 2 — Run makemigrations interactively#
Run:
python manage.py makemigrations
Django should detect the change and ask:
Did you rename book.pub_date to book.published_date (a DateField)? [y/N]
Answer:
y
This tells Django:
This is the same field. Only its name changed.
Django can then generate a RenameField migration instead of a destructive RemoveField + AddField migration.
Step 3 — Inspect the generated migration#
Always open the generated migration file before applying it.
You want to see:
class Migration(migrations.Migration):
dependencies = [
("myapp", "0004_previous"),
]
operations = [
migrations.RenameField(
model_name="book",
old_name="pub_date",
new_name="published_date",
),
]
The important part is:
migrations.RenameField(...)
You do not want:
migrations.RemoveField(...)
migrations.AddField(...)
A RenameField tells Django to rename the existing database column rather than remove it and create a new one.
For example, PostgreSQL can perform an operation conceptually equivalent to:
ALTER TABLE myapp_book
RENAME COLUMN pub_date TO published_date;
The existing values remain intact.
Step 4 — Apply the migration#
Once you've verified the migration, run:
python manage.py migrate
The column is renamed and the existing data remains in place.
Tip: You can inspect the SQL Django plans to execute with
sqlmigrate:python manage.py sqlmigrate myapp 0005This is especially useful when reviewing schema changes before deploying them to production.
Part 2: What If You Miss the Rename Prompt?#
This is where you need to be careful.
Suppose you run:
python manage.py makemigrations --noinput
or accidentally accept the default answer when Django asks whether a field was renamed.
Django may generate a migration containing:
migrations.RemoveField(...)
migrations.AddField(...)
If you see this for what should be a simple rename:
Do not run migrate.
Instead:
- Delete the incorrect migration file if it has not been applied.
- Run
makemigrationsagain interactively. - Confirm the rename prompt with
y. - Inspect the resulting migration.
- Only then run
migrate.
You can also manually correct an unapplied migration.
For example, change:
migrations.RemoveField(
model_name="book",
name="pub_date",
),
migrations.AddField(
model_name="book",
name="published_date",
field=models.DateField(),
),
to:
migrations.RenameField(
model_name="book",
old_name="pub_date",
new_name="published_date",
),
Migrations are Python code, so manually editing an unapplied migration is supported.
The important rule is:
Never casually replace an already-applied migration. If other environments have already run it, create a new migration instead.
Part 3: Renaming a Model#
Renaming a model is similar, but there are more things to consider.
A model rename can affect:
- The Python model class
- The database table
- ForeignKey relationships
- Many-to-many relationships
- Admin configuration
- Serializers
- Forms
- Views
- Imports
- Queries
- Raw SQL
- Other application code
Suppose you want to rename:
Book
to:
Publication
Step 1 — Rename the model class#
Change:
class Book(models.Model):
title = models.CharField(max_length=200)
published_date = models.DateField()
to:
class Publication(models.Model):
title = models.CharField(max_length=200)
published_date = models.DateField()
Then update references throughout your application.
For example:
from .models import Book
becomes:
from .models import Publication
You should also check:
- Django admin
- Serializers
- Forms
- Views
- Services
- Tasks
- ForeignKeys
- ManyToManyFields
- Tests
- Raw SQL
- Type hints
- Imports
Step 2 — Run makemigrations#
Run:
python manage.py makemigrations
Django may ask:
Did you rename the myapp.Book model to Publication? [y/N]
Answer:
y
Django can then generate a RenameModel operation:
migrations.RenameModel(
old_name="Book",
new_name="Publication",
),
Step 3 — Verify the migration#
Check the generated migration before applying it.
You should see:
migrations.RenameModel(
old_name="Book",
new_name="Publication",
)
rather than an operation that deletes the old model and creates a completely new one.
Step 4 — Apply the migration#
After reviewing the migration:
python manage.py migrate
Django will rename the model at the migration/state level and handle the corresponding database table rename.
The important distinction is that database schema changes and Python code changes are related, but they're not the same thing.
Django can update its migration state and database schema, but you still need to update your application's Python references.
Keeping the Existing Database Table Name#
Sometimes you want to rename the Python model without renaming the physical database table.
For example, perhaps your application has:
myapp_book
and you want the Python model to become:
class Publication(models.Model):
...
but you want the database table to remain:
myapp_book
You can explicitly specify the table name:
class Publication(models.Model):
title = models.CharField(max_length=200)
published_date = models.DateField()
class Meta:
db_table = "myapp_book"
This separates the Python model name from the database table name.
This can be useful when the database table name is part of an external integration, reporting system, or legacy schema that you don't want to change.
Note: Don't assume that adding
db_tableis always a complete no-op migration. Always inspect the migration Django generates and the SQL withsqlmigrate, especially when changing an existing model.
Part 4: Renaming a Model with ForeignKeys#
Model renames become more interesting when other models reference the model being renamed.
Suppose you have:
class Book(models.Model):
title = models.CharField(max_length=200)
and:
class Review(models.Model):
book = models.ForeignKey(
"Book",
on_delete=models.CASCADE,
)
You want to rename Book to Publication.
You should update the ForeignKey reference:
class Review(models.Model):
publication = models.ForeignKey(
"Publication",
on_delete=models.CASCADE,
)
There are actually two different changes here.
First:
Book → Publication
This is a model rename.
Second:
Review.book → Review.publication
This is a field rename.
Django may therefore generate both:
migrations.RenameModel(
old_name="Book",
new_name="Publication",
),
migrations.RenameField(
model_name="review",
old_name="book",
new_name="publication",
),
Handle each migration operation deliberately.
Don't Forget Python References#
Even if Django handles the database relationship, your application code still needs to be updated.
For example:
Review.objects.filter(book=book)
may need to become:
Review.objects.filter(publication=publication)
Likewise, check:
Review.objects.select_related("book")
and:
Review.objects.filter(book__title="Django")
These are application-level references and Django cannot automatically update every piece of application code for you.
Part 5: Zero-Downtime Renames in Production#
The simple rename approach works well when you can safely deploy the application and migration together.
However, production systems often use rolling deployments.
That creates a problem.
Imagine your current application uses:
pub_date
and you deploy a new version that expects:
published_date
During the deployment, both versions of your application might temporarily be running:
Old application → expects pub_date
New application → expects published_date
If you immediately rename the database column:
pub_date → published_date
the old application may start failing because its SQL still references:
pub_date
This is why production systems often use the expand/contract pattern.
The idea is simple:
Expand
↓
Add new structure
↓
Backfill data
↓
Dual-write
↓
Switch reads
↓
Contract
↓
Remove old structure
Instead of renaming the existing column immediately, you introduce the new column gradually.
Expand/Contract Field Rename#
Let's rename:
pub_date → published_date
Deploy 1 — Expand#
First, add the new column while keeping the old one.
class Book(models.Model):
pub_date = models.DateField()
published_date = models.DateField(null=True)
Generate the migration:
python manage.py makemigrations
This produces an AddField operation.
At this point the database contains both columns:
pub_date
published_date
Nothing has been removed yet.
This is important because both old and new application versions can still operate.
Deploy 2 — Backfill Existing Data#
Next, copy the existing values from the old column into the new one.
A data migration can do this:
from django.db import migrations, models
def copy_dates(apps, schema_editor):
Book = apps.get_model("myapp", "Book")
Book.objects.update(
published_date=models.F("pub_date"),
)
class Migration(migrations.Migration):
dependencies = [
("myapp", "0006_book_published_date"),
]
operations = [
migrations.RunPython(
copy_dates,
migrations.RunPython.noop,
),
]
After the migration, existing records have values in both columns:
pub_date published_date
---------- ----------------
2026-01-01 2026-01-01
2026-02-15 2026-02-15
2026-03-20 2026-03-20
For large production tables, avoid blindly performing a massive update in one transaction. Consider batching the backfill or using an approach appropriate for your database size and deployment environment.
Deploy 3 — Dual-Write#
Now update the application so new changes are written to both fields.
Conceptually:
book.pub_date = value
book.published_date = value
book.save()
During this stage, both columns remain synchronized.
This gives older application instances access to:
pub_date
while newer instances can use:
published_date
The exact implementation of dual-writing depends on your application architecture. For high-volume systems, you should also consider how bulk updates, background jobs, direct SQL, and integrations write to the data.
Deploy 4 — Switch Reads#
Once you're confident the new column is populated and being kept up to date, change the application to read from:
published_date
instead of:
pub_date
For example:
Book.objects.filter(
published_date__gte=start_date,
)
The old column still exists, but the application no longer depends on it.
Deploy 5 — Contract#
After you've verified that no running application, background task, integration, report, or SQL query still depends on pub_date, remove it.
Your final model becomes:
class Book(models.Model):
published_date = models.DateField()
Then generate a migration containing:
migrations.RemoveField(
model_name="book",
name="pub_date",
)
At this point, the old column can safely be removed.
Why Expand/Contract Works#
The key idea is that every intermediate database state is compatible with the application versions that may be running during deployment.
Instead of:
Old code
↓
Rename database column
↓
New code
you use:
Add new column
↓
Backfill
↓
Dual-write
↓
Switch reads
↓
Remove old column
This is especially useful for:
- Rolling deployments
- Kubernetes deployments
- Multiple application instances
- Large production databases
- High-traffic APIs
- Systems where downtime isn't acceptable
The same general strategy can be applied to model/table renames:
Create new table
↓
Backfill data
↓
Dual-write
↓
Switch reads
↓
Remove old table
The implementation is more complicated than a simple RenameModel, but it provides much stronger compatibility during a rolling deployment.
Common Pitfalls#
1. Don't blindly run migrate#
Before applying a rename migration, inspect it.
Look for:
migrations.RenameField(...)
or:
migrations.RenameModel(...)
Be suspicious if a simple rename unexpectedly produces:
migrations.RemoveField(...)
migrations.AddField(...)
2. Be Careful with --noinput#
Automated environments often run commands non-interactively.
For example:
python manage.py makemigrations --noinput
This means Django cannot ask you to confirm a possible rename.
A good practice is to generate migrations during development, review them, and commit them to version control rather than relying on production or CI environments to discover schema changes.
3. Search for Every Code Reference#
Renaming a field isn't just a models.py change.
Search your codebase for the old name.
For example:
rg "pub_date"
Check:
- Models
- Serializers
- Views
- Services
- Forms
- Admin
- Tests
- Celery tasks
- Management commands
- Templates
- Raw SQL
F()expressionsQ()expressionsselect_related()prefetch_related()- Filters
- Ordering
- Reporting queries
For model renames, also search for string references:
ForeignKey("Book", ...)
ManyToManyField("Book", ...)
4. Test With Realistic Data#
A migration that works against an empty development database doesn't necessarily prove that it is safe for production.
Test migrations against a representative copy of your production data whenever possible.
Pay particular attention to:
- Existing records
- Null values
- ForeignKeys
- Large tables
- Unique constraints
- Indexes
- Database triggers
- Raw SQL
- External integrations
5. Think About Large Tables#
A schema operation that takes milliseconds on a development database may behave very differently against a table containing millions of rows.
Before applying a migration to a large production table, understand:
- Whether the operation requires a table rewrite
- Whether it acquires locks
- How long those locks may be held
- Whether indexes need to be rebuilt
- Whether the migration runs inside a transaction
- Whether your deployment can tolerate the resulting load
For critical production systems, test the migration against a realistic database size before deployment.
6. Back Up Before Risky Schema Changes#
Before applying a schema change to production, make sure you have a reliable backup or database snapshot.
A migration should be designed to be safe, but backups provide an additional recovery mechanism if something unexpected happens.
A Practical Rename Checklist#
Before merging a rename migration, go through this checklist:
- Rename the field/model in
models.py. - Search the codebase for references to the old name.
- Update serializers, views, services, forms, and admin.
- Update ForeignKeys and ManyToMany relationships.
- Run
makemigrationsinteractively. - Confirm Django recognizes the change as a rename.
- Inspect the generated migration.
- Run
sqlmigrateto inspect the SQL. - Test the migration against realistic data.
- Consider locking and performance implications for large tables.
- Take a production backup or snapshot.
- For rolling deployments, use expand/contract.
- Only remove the old field after all application versions no longer depend on it.
Key Takeaways#
Renaming a Django model or field is easy when you're working locally, but it deserves more attention when the database contains real production data.
The most important rules are:
- Use
RenameFieldfor field renames. - Use
RenameModelfor model renames. - Always inspect generated migrations before running them.
- Don't blindly accept
RemoveField+AddFieldwhen you intended a rename. - Update application-level references yourself.
- Use
db_tablewhen you need to keep an existing physical table name. - For rolling deployments, prefer the expand/contract pattern.
- Test migrations against realistic production-like data.
- Consider locks and migration performance for large tables.
- Keep backups available before applying important production schema changes.
The habit that will save you the most trouble is simple:
Never treat a migration as something Django generated that you can blindly execute. Treat it as code that deserves review.
Run makemigrations, read what Django generated, inspect the SQL when necessary, and make sure the migration matches the change you actually intended.
Do that consistently, and database renames become a controlled engineering task instead of a production gamble.#
Final Thoughts#
A model or field rename may look like a tiny code change:
Book.pub_date
↓
Book.published_date
But the database doesn't know that you meant "rename."
It only sees schema changes.
That's why understanding Django migrations matters. Once you know what Django is actually generating and how those operations affect the database, you can make schema changes confidently — even in large production systems.
A migration file is not just a file Django generates for you. It's part of your application's database code. Review it like you would review any other important piece of code.#
Discussion
Loading the discussion…
Related articles
- How Django Migrations Actually Work: From makemigrations to migrateA deep dive into how Django migrations really work how makemigrations detects model changes, how the migration graph and autodetector operate, how migrate turns operations into SQL via the SchemaEditoSep 15, 2026 · Django Migration
- How to Find and Fix N+1 Queries in DjangoA practical guide to finding N+1 query problems in Django with django-debug-toolbar and assertNumQueries, and fixing them with select_related, prefetch_related and Prefetch.Aug 30, 2026 · Django
- Redis Caching in Django: A Practical GuideHow to add Redis caching to a Django application without shipping stale data: cache backends, key design, the cache-aside pattern, invalidation on save and stampede protection.Aug 28, 2026 · Redis