summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2014-08-03 15:02:40 -0400
committerSimon Charette <charette.s@gmail.com>2014-08-03 15:27:01 -0400
commitcbb29af1aa1ebe11b38e73a6f34df9cd171d6c5b (patch)
treecf295145dbc4f9027d26db2553f9bfff1fbba49e
parent2d542bf60cc37c59872c5a20d6af3bf826038739 (diff)
Fixed #23160 -- Correctly rename models with self referential fields.
Thanks to whitews AT gmail for the report.
-rw-r--r--django/db/migrations/operations/models.py26
-rw-r--r--tests/migrations/test_operations.py37
2 files changed, 56 insertions, 7 deletions
diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py
index 4e8100a5bb..5b1a23eab7 100644
--- a/django/db/migrations/operations/models.py
+++ b/django/db/migrations/operations/models.py
@@ -124,10 +124,14 @@ 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):
- related_key = (
- related_object.model._meta.app_label,
- related_object.model._meta.object_name.lower(),
- )
+ # 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(),
+ )
new_fields = []
for name, field in state.models[related_key].fields:
if name == related_object.field.name:
@@ -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):
+ 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_object.model._meta.app_label,
- related_object.model._meta.object_name.lower(),
+ *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,
)
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 769d0be417..405157f8d6 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -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.