Renaming models
How autodetection handles a renamed model, and how to resolve an ambiguous rename-vs-drop safely.
Renaming models
When you rename a model, makemigrations has to decide whether you renamed an existing table (keep its rows) or dropped one and created another (start empty). It uses two signals.
Struct-name match — resolved automatically
If the Rust struct keeps its name but its table changes (e.g. you set a new #[umbral(table = "...")]), that's an unambiguous rename. Autodetection emits a RenameTable and moves the rows. Nothing to do.
Column-shape match — you decide
If both the struct name and the table change, autodetection can only compare column shapes. A dropped model and a new model with a bit-identical shape is genuinely ambiguous:
- it could be a rename (move the old rows to the new name), or
- two unrelated models that happen to share a shape (drop the old, create the new empty).
Guessing either way is a silent data bug — auto-renaming hands one model's rows to another and skips the intended drop; auto-dropping deletes rows you meant to keep. So makemigrations fails closed and asks you to choose:
umbral makemigrations: ambiguous rename — the dropped model `foo` and the newmodel `bar` have identical column shapes ... Resolve it: setUMBRAL_MIGRATIONS_ASSUME_RENAMES=assume ... or =independent ...Resolve it with one environment variable:
Renaming a model with an M2M field
A model's many-to-many junction table is named <parent_table>_<field> (e.g. an Article with a tags: M2M<Tag> field owns article_tags). When you rename the parent — article → post — the autodetector renames the junction too (article_tags → post_tags) instead of dropping and recreating it, so every relationship row is preserved. No extra step is required; makemigrations emits the RenameTable for the junction automatically alongside the parent's rename.
This works because the junction's columns are generic (parent_id / child_id, not article_id) and its foreign key to the parent is updated by the parent's own rename, so a table rename is all that's needed.
See also
- Managed migrations — the declare → migrate loop.
- Design rationale:
planning/audit_2/AUDIT_REPORT.md(finding H23) andarch.md(autodetection).