Loading django/db/migrations/operations/models.py +19 −7 Original line number Diff line number Diff line Loading @@ -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(), Loading Loading @@ -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, ) Loading tests/migrations/test_operations.py +37 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ class OperationTestBase(MigrationTestBase): [ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey("Pony")), ("friend", models.ForeignKey("self")) ], )) if mti_model: Loading Loading @@ -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. Loading Loading
django/db/migrations/operations/models.py +19 −7 Original line number Diff line number Diff line Loading @@ -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(), Loading Loading @@ -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, ) Loading
tests/migrations/test_operations.py +37 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ class OperationTestBase(MigrationTestBase): [ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey("Pony")), ("friend", models.ForeignKey("self")) ], )) if mti_model: Loading Loading @@ -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. Loading