Skip to content
>_Rong

Search the full text of every article — titles, tags, categories and article content.

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.

saroeun12 min readDjango Migration
On this page

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:

  1. A field called pub_date disappeared.
  2. A field called published_date appeared.

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 0005
 

This 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:

  1. Delete the incorrect migration file if it has not been applied.
  2. Run makemigrations again interactively.
  3. Confirm the rename prompt with y.
  4. Inspect the resulting migration.
  5. 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_table is always a complete no-op migration. Always inspect the migration Django generates and the SQL with sqlmigrate, 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() expressions
  • Q() expressions
  • select_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 makemigrations interactively.
  • Confirm Django recognizes the change as a rename.
  • Inspect the generated migration.
  • Run sqlmigrate to 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:

  1. Use RenameField for field renames.
  2. Use RenameModel for model renames.
  3. Always inspect generated migrations before running them.
  4. Don't blindly accept RemoveField + AddField when you intended a rename.
  5. Update application-level references yourself.
  6. Use db_table when you need to keep an existing physical table name.
  7. For rolling deployments, prefer the expand/contract pattern.
  8. Test migrations against realistic production-like data.
  9. Consider locks and migration performance for large tables.
  10. 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…