In Django, how do you rename a model field live when the previous release keeps querying the old column during the deploy?
answer
- what RenameField does to the column
- old code names every column
- rename the attribute, not the column
- db_column and a later drop
basics
~20 sAvoid RenameField's RENAME COLUMN while old code runs. Either rename only the Python attribute and keep db_column pointing at the old column, or change the column across releases: add, write both, backfill, switch reads, then drop.
solid answer
~40 s`RenameField` becomes `ALTER TABLE ... RENAME COLUMN`, and the release still serving traffic names the old column explicitly in every SELECT and INSERT, so it fails from the moment `migrate` runs. The cheapest fix is often not to rename the column at all: rename the attribute and set `db_column="data"`; `makemigrations` asks whether the field was renamed and writes an `AlterField` plus `RenameField` that change no column, which `sqlmigrate` confirms. If the column itself must change, spread it over releases: add the new field nullable or with `db_default`, write both fields, backfill, switch reads, then make the old field nullable, remove it from the model with a state-only `SeparateDatabaseAndState`, and drop the column in a later release.
code
python · 6 linesfrom django.db import models
class Event(models.Model):
# renamed from `data`; the column keeps its old name
payload = models.JSONField(db_column="data")go deeper
Know that RenameField renames the database column and that db_column lets the attribute and the column have different names.
Explain why explicit column lists make a column rename break the running release, and what makemigrations writes for an attribute-only rename.
Lay out the multi-release column change: dual writes, backfill, read switch, nullable old field, state-only removal, and the later RunSQL drop.
Decide when a naming change justifies several deploys and when db_column is the pragmatic answer, and set that expectation for the team.
## Why RenameField breaks a rolling deploy Django's ORM never uses `SELECT *`: every query lists the model's columns by name, and every `INSERT` lists the fields it writes. The previous release was built against the old model, so its SQL references `analytics_event.data`. `RenameField` on PostgreSQL runs `ALTER TABLE "analytics_event" RENAME COLUMN "data" TO "payload"`. The statement itself is quick, but from that moment every query from old processes fails with an unknown column until the last old process is gone. Deploying code first does not help either: the new code would query `payload` before it exists. ## Option 1: rename the attribute, keep the column Most renames are about readable Python, not the column name. Django separates the two with `db_column`: 1. Rename `data` to `payload` in the model and add `db_column="data"`. 2. Run `makemigrations`. It asks `Was event.data renamed to event.payload (a JSONField)? [y/N]`; answer yes. 3. The autodetector writes an `AlterField` that sets `db_column` to the existing column name and a `RenameField`. Because the column name is unchanged, neither emits a rename; `sqlmigrate` shows no `RENAME COLUMN`. 4. Deploy. Old and new releases both use the column `data`. The cost is a permanent mismatch between the attribute and the column name, which only matters to people writing raw SQL. ## Option 2: change the column over several releases When the column really must change (a new type, a new meaning, or a naming standard), treat it as adding a new field and retiring the old one. | Release | Model and migrations | Old column | New column | |---|---|---|---| | 1 | add `payload` with `null=True` or `db_default`; code writes both fields | read and written | written | | 2 | backfill `payload` in a data migration; code reads `payload` | written | read and written | | 3 | make `data` nullable; stop writing it; remove `data` from the model with a state-only `SeparateDatabaseAndState` | still in the table, unused | read and written | | 4 | drop the `data` column with `RunSQL` | gone | only column | ## Retiring the old field safely - **Make it nullable first.** Once `data` is gone from the model, new inserts omit it; a `NOT NULL` column without a database default would reject them. - **Remove it from state, not from the table.** `SeparateDatabaseAndState(state_operations=[RemoveField(...)], database_operations=[])` updates Django's model state so the autodetector is satisfied, while the column stays for the previous release that still selects it. - **Drop the column one release later**, when no running code references it, with `RunSQL` (a `RemoveField` there would fail, because the field is no longer in migration state). ## Choosing between them - Pure naming change: option 1, one deploy, no data movement. - Type or meaning change, or a mandated column name: option 2, accepting several deploys. - A maintenance window where no old code runs: a plain `RenameField` is fine. The interviewer wants to hear that Django's explicit column lists are why a rename is not zero-downtime, and that `db_column` plus state-only operations are the Django tools that make it so.
- Why not deploy the new code first and run RenameField afterwards?The new code queries the new column name, which does not exist until the migration runs, so the same failure happens in the other direction. Any order that has one release referencing a column the other lacks breaks requests; only a period where both names exist, or no rename, avoids it.
- Why must the old field be nullable before it is removed from the model?After removal, Django's INSERTs omit that column. If it is still `NOT NULL` with no database default, every new row is rejected. Making it nullable, or giving it `db_default`, one release earlier keeps inserts valid until the column is dropped.
saying these in an interview costs you the question
- RenameField is safe because renaming a column is instant
- Django selects with SELECT star, so renamed columns are picked up
- db_column on a renamed attribute forces a column rename
- Removing a field from the model and dropping its column in one release is safe
- A NOT NULL column can stay NOT NULL after its field is removed