Commit 6b2473d3 authored by Simon Charette's avatar Simon Charette
Browse files

[1.7.x] Fixed #23160 -- Correctly rename models with self referential fields.

Thanks to whitews AT gmail for the report.

Backport of cbb29af1 from master
parent 1cb919e4
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -124,6 +124,10 @@ class RenameModel(Operation):
        del state.models[app_label, self.old_name.lower()]
        # Repoint the FKs and M2Ms pointing to us
        for related_object in (related_objects + related_m2m_objects):
            # Use the new related key for self referential related objects.
            if related_object.model == model:
                related_key = (app_label, self.new_name.lower())
            else:
                related_key = (
                    related_object.model._meta.app_label,
                    related_object.model._meta.object_name.lower(),
@@ -152,12 +156,20 @@ class RenameModel(Operation):
            related_objects = old_model._meta.get_all_related_objects()
            related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
            for related_object in (related_objects + related_m2m_objects):
                to_field = new_apps.get_model(
                if related_object.model == old_model:
                    model = new_model
                    related_key = (app_label, self.new_name.lower())
                else:
                    model = related_object.model
                    related_key = (
                        related_object.model._meta.app_label,
                        related_object.model._meta.object_name.lower(),
                    )
                to_field = new_apps.get_model(
                    *related_key
                )._meta.get_field_by_name(related_object.field.name)[0]
                schema_editor.alter_field(
                    related_object.model,
                    model,
                    related_object.field,
                    to_field,
                )
+37 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ class OperationTestBase(MigrationTestBase):
                [
                    ("id", models.AutoField(primary_key=True)),
                    ("pony", models.ForeignKey("Pony")),
                    ("friend", models.ForeignKey("self"))
                ],
            ))
        if mti_model:
@@ -406,6 +407,42 @@ class OperationTests(OperationTestBase):
            self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id"))
            self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id"))

    def test_rename_model_with_self_referential_fk(self):
        """
        Tests the RenameModel operation on model with self referential FK.
        """
        project_state = self.set_up_test_model("test_rmwsrf", related_model=True)
        # Test the state alteration
        operation = migrations.RenameModel("Rider", "HorseRider")
        self.assertEqual(operation.describe(), "Rename model Rider to HorseRider")
        new_state = project_state.clone()
        operation.state_forwards("test_rmwsrf", new_state)
        self.assertNotIn(("test_rmwsrf", "rider"), new_state.models)
        self.assertIn(("test_rmwsrf", "horserider"), new_state.models)
        # Remember, RenameModel also repoints all incoming FKs and M2Ms
        self.assertEqual("test_rmwsrf.HorseRider", new_state.models["test_rmwsrf", "horserider"].fields[2][1].rel.to)
        # Test the database alteration
        self.assertTableExists("test_rmwsrf_rider")
        self.assertTableNotExists("test_rmwsrf_horserider")
        if connection.features.supports_foreign_keys:
            self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id"))
            self.assertFKNotExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_horserider", "id"))
        with connection.schema_editor() as editor:
            operation.database_forwards("test_rmwsrf", editor, project_state, new_state)
        self.assertTableNotExists("test_rmwsrf_rider")
        self.assertTableExists("test_rmwsrf_horserider")
        if connection.features.supports_foreign_keys:
            self.assertFKNotExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id"))
            self.assertFKExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_horserider", "id"))
        # And test reversal
        with connection.schema_editor() as editor:
            operation.database_backwards("test_rmwsrf", editor, new_state, project_state)
        self.assertTableExists("test_rmwsrf_rider")
        self.assertTableNotExists("test_rmwsrf_horserider")
        if connection.features.supports_foreign_keys:
            self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id"))
            self.assertFKNotExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_horserider", "id"))

    def test_add_field(self):
        """
        Tests the AddField operation.