From d8e03fdeb9613b996e52bb5e7f083f7284005600 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 28 Oct 2018 18:21:25 -0400 Subject: [PATCH] Fixed #29897 -- Fixed autodetector's swappable MTI dependency resolution. Thanks Steven Ganz for the detailed report. --- django/db/migrations/autodetector.py | 3 +++ tests/migrations/test_autodetector.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 4cde51e018..fa63745b48 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -345,6 +345,9 @@ class MigrationAutodetector: dependency_graph = {op: set() for op in ops} for op in ops: for dep in op._auto_deps: + # Resolve intra-app dependencies to handle circular + # references involving a swappable model. + dep = self._resolve_dependency(dep)[0] if dep[0] == app_label: for op2 in ops: if self.check_dependency(op2, dep): diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py index 66470526a7..7a6a990b12 100644 --- a/tests/migrations/test_autodetector.py +++ b/tests/migrations/test_autodetector.py @@ -2348,6 +2348,18 @@ class AutodetectorTests(TestCase): self.assertOperationTypes(changes, 'a', 0, ["CreateModel"]) self.assertMigrationDependencies(changes, 'a', 0, []) + @override_settings(AUTH_USER_MODEL='a.User') + def test_swappable_circular_multi_mti(self): + with isolate_lru_cache(apps.get_swappable_settings_name): + parent = ModelState('a', 'Parent', [ + ('user', models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE)) + ]) + child = ModelState('a', 'Child', [], bases=('a.Parent',)) + user = ModelState('a', 'User', [], bases=(AbstractBaseUser, 'a.Child')) + changes = self.get_changes([], [parent, child, user]) + self.assertNumberMigrations(changes, 'a', 1) + self.assertOperationTypes(changes, 'a', 0, ['CreateModel', 'CreateModel', 'CreateModel', 'AddField']) + @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition', side_effect=AssertionError("Should not have prompted for not null addition")) def test_add_blank_textfield_and_charfield(self, mocked_ask_method):