From b84ecaa736fd5e33bd5d95ac70e288db976a53ff Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Tue, 12 Jul 2016 18:45:17 -0700 Subject: [PATCH] Fixed #26088 -- Made autodector detect changing proxy model to MTI. --- django/db/migrations/autodetector.py | 14 ++++++ tests/migrations/test_autodetector.py | 72 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index ae88a90465..c863dcaead 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -612,6 +612,20 @@ class MigrationAutodetector(object): ] ) + # Fix relationships if the model changed from a proxy model to a + # concrete model. + if (app_label, model_name) in self.old_proxy_keys: + for related_object in model_opts.related_objects: + self.add_operation( + related_object.related_model._meta.app_label, + operations.AlterField( + model_name=related_object.related_model._meta.object_name, + name=related_object.field.name, + field=related_object.field, + ), + dependencies=[(app_label, model_name, None, True)], + ) + def generate_created_proxies(self): """ Makes CreateModel statements for proxy models. diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py index 68c4e85b6d..7265330057 100644 --- a/tests/migrations/test_autodetector.py +++ b/tests/migrations/test_autodetector.py @@ -240,6 +240,7 @@ class AutodetectorTests(TestCase): }, ("testapp.author", )) author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {}, ("testapp.author", )) author_proxy_third = ModelState("thirdapp", "AuthorProxy", [], {"proxy": True}, ("testapp.author", )) + author_proxy_third_notproxy = ModelState("thirdapp", "AuthorProxy", [], {}, ("testapp.author", )) author_proxy_proxy = ModelState("testapp", "AAuthorProxyProxy", [], {"proxy": True}, ("testapp.authorproxy", )) author_unmanaged = ModelState("testapp", "AuthorUnmanaged", [], {"managed": False}, ("testapp.author", )) author_unmanaged_managed = ModelState("testapp", "AuthorUnmanaged", [], {}, ("testapp.author", )) @@ -336,6 +337,10 @@ class AutodetectorTests(TestCase): ("author", models.ForeignKey("thirdapp.AuthorProxy", models.CASCADE)), ("title", models.CharField(max_length=200)), ]) + book_proxy_proxy_fk = ModelState("otherapp", "Book", [ + ("id", models.AutoField(primary_key=True)), + ("author", models.ForeignKey("testapp.AAuthorProxyProxy", models.CASCADE)), + ]) book_migrations_fk = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.UnmigratedModel", models.CASCADE)), @@ -1291,6 +1296,73 @@ class AutodetectorTests(TestCase): # The field name the FK on the book model points to self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'pk_field') + def test_proxy_to_mti_with_fk_to_proxy(self): + # First, test the pk table and field name. + changes = self.get_changes( + [], + [self.author_empty, self.author_proxy_third, self.book_proxy_fk], + ) + self.assertEqual( + changes['otherapp'][0].operations[0].fields[2][1].remote_field.model._meta.db_table, + 'testapp_author', + ) + self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id') + + # Change AuthorProxy to use MTI. + changes = self.get_changes( + [self.author_empty, self.author_proxy_third, self.book_proxy_fk], + [self.author_empty, self.author_proxy_third_notproxy, self.book_proxy_fk], + ) + # Right number/type of migrations for the AuthorProxy model? + self.assertNumberMigrations(changes, 'thirdapp', 1) + self.assertOperationTypes(changes, 'thirdapp', 0, ['DeleteModel', 'CreateModel']) + # Right number/type of migrations for the Book model with a FK to + # AuthorProxy? + self.assertNumberMigrations(changes, 'otherapp', 1) + self.assertOperationTypes(changes, 'otherapp', 0, ['AlterField']) + # otherapp should depend on thirdapp. + self.assertMigrationDependencies(changes, 'otherapp', 0, [('thirdapp', 'auto_1')]) + # Now, test the pk table and field name. + self.assertEqual( + changes['otherapp'][0].operations[0].field.remote_field.model._meta.db_table, + 'thirdapp_authorproxy', + ) + self.assertEqual(changes['otherapp'][0].operations[0].field.remote_field.field_name, 'author_ptr') + + def test_proxy_to_mti_with_fk_to_proxy_proxy(self): + # First, test the pk table and field name. + changes = self.get_changes( + [], + [self.author_empty, self.author_proxy, self.author_proxy_proxy, self.book_proxy_proxy_fk], + ) + self.assertEqual( + changes['otherapp'][0].operations[0].fields[1][1].remote_field.model._meta.db_table, + 'testapp_author', + ) + self.assertEqual(changes['otherapp'][0].operations[0].fields[1][1].remote_field.field_name, 'id') + + # Change AuthorProxy to use MTI. FK still points to AAuthorProxyProxy, + # a proxy of AuthorProxy. + changes = self.get_changes( + [self.author_empty, self.author_proxy, self.author_proxy_proxy, self.book_proxy_proxy_fk], + [self.author_empty, self.author_proxy_notproxy, self.author_proxy_proxy, self.book_proxy_proxy_fk], + ) + # Right number/type of migrations for the AuthorProxy model? + self.assertNumberMigrations(changes, 'testapp', 1) + self.assertOperationTypes(changes, 'testapp', 0, ['DeleteModel', 'CreateModel']) + # Right number/type of migrations for the Book model with a FK to + # AAuthorProxyProxy? + self.assertNumberMigrations(changes, 'otherapp', 1) + self.assertOperationTypes(changes, 'otherapp', 0, ['AlterField']) + # otherapp should depend on testapp. + self.assertMigrationDependencies(changes, 'otherapp', 0, [('testapp', 'auto_1')]) + # Now, test the pk table and field name. + self.assertEqual( + changes['otherapp'][0].operations[0].field.remote_field.model._meta.db_table, + 'testapp_authorproxy', + ) + self.assertEqual(changes['otherapp'][0].operations[0].field.remote_field.field_name, 'author_ptr') + def test_unmanaged_create(self): """Tests that the autodetector correctly deals with managed models.""" # First, we test adding an unmanaged model