Opened 39 minutes ago

Last modified 39 minutes ago

#37477 new Bug

makemigrations orders AlterModelOptions before RenameModel when a new model has an FK to the renamed model

Reported by: Akshat Gupta Owned by:
Component: Migrations Version: dev
Severity: Normal Keywords: autodetector, RenameModel
Cc: Akshat Gupta Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When a model is renamed, `makemigrations` can put operations on the new model name before the `RenameModel` operation. `migrate` then crashes with a `KeyError`.

It needs three things in one `makemigrations` run:

1. A model is renamed (and the rename prompt is answered "y").
2. Its Meta options change, for example `ordering`.
3. A new model with a ForeignKey to the renamed model is added.

Starting point (migrated):
class Author(models.Model):
    name = models.CharField(max_length=50)
Changed to:
class Writer(models.Model):
    name = models.CharField(max_length=50)

    class Meta:
        ordering = ["name"]


class Book(models.Model):
    title = models.CharField(max_length=50)
    writer = models.ForeignKey(Writer, on_delete=models.CASCADE)
`makemigrations` ("Was the model shop.Author renamed to Writer? y") writes the operations in this order:
operations = [
    migrations.AlterModelOptions(name="writer", options={"ordering": ["name"]}),
    migrations.RenameModel(old_name="Author", new_name="Writer"),
    migrations.CreateModel(name="Book", ...),
]
`migrate` fails:

    File ".../django/db/migrations/state.py", line 183, in alter_model_options
        model_state = self.models[app_label, model_name]
    KeyError: ('shop', 'writer')

Without the new `Book` model the order is correct. I also see `AddIndex` and `AddConstraint` on the renamed model emitted before `RenameModel` in the same situation.

Cause: `generate_renamed_models()` gives `RenameModel` dependencies for the reverse relations of the renamed model. A new model with an FK to it makes `RenameModel` depend on that `CreateModel`. The options, index and constraint operations on the renamed model have no dependency on `RenameModel`. `_sort_migrations()` emits operations without dependencies first, so they end up before the rename.

A fix could make every operation on a renamed model depend on its `RenameModel` in `_sort_migrations()`. I tried this locally and the migrations tests pass.

Tested on main (6.2 dev) with SQLite.

Change History (1)

comment:1 by Akshat Gupta, 39 minutes ago

Component: Uncategorized → Migrations
Note: See TracTickets for help on using tickets.
Back to Top