From cb60d22bd94738174440f8d60a04c3d9c38636c9 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Mon, 28 Jul 2014 10:32:43 -0700 Subject: [PATCH] Fixed #23100: Individual FK creation missing dependencies --- django/db/migrations/autodetector.py | 27 ++++++++++++++++++++++++--- tests/migrations/test_autodetector.py | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 7f52b9d07c..dfbfe869fd 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -699,7 +699,7 @@ class MigrationAutodetector(object): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] - field = new_model_state.get_field_by_name(field_name) + field = self.new_apps.get_model(app_label, model_name)._meta.get_field_by_name(field_name)[0] # Scan to see if this is actually a rename! field_dec = self.deep_deconstruct(field) found_rename = False @@ -727,6 +727,25 @@ class MigrationAutodetector(object): break if found_rename: continue + # Fields that are foreignkeys/m2ms depend on stuff + dependencies = [] + if field.rel and field.rel.to: + # Account for FKs to swappable models + swappable_setting = getattr(field, 'swappable_setting', None) + if swappable_setting is not None: + dep_app_label = "__setting__" + dep_object_name = swappable_setting + else: + dep_app_label = field.rel.to._meta.app_label + dep_object_name = field.rel.to._meta.object_name + dependencies = [(dep_app_label, dep_object_name, None, True)] + if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created: + dependencies.append(( + field.rel.through._meta.app_label, + field.rel.through._meta.object_name, + None, + True + )) # You can't just add NOT NULL fields with no default if not field.null and not field.has_default() and not isinstance(field, models.ManyToManyField): field = field.clone() @@ -738,7 +757,8 @@ class MigrationAutodetector(object): name=field_name, field=field, preserve_default=False, - ) + ), + dependencies=dependencies, ) else: self.add_operation( @@ -747,7 +767,8 @@ class MigrationAutodetector(object): model_name=model_name, name=field_name, field=field, - ) + ), + dependencies=dependencies, ) def generate_removed_fields(self): diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py index 27f4af8ad9..8b13269d77 100644 --- a/tests/migrations/test_autodetector.py +++ b/tests/migrations/test_autodetector.py @@ -1105,3 +1105,18 @@ class AutodetectorTests(TestCase): self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Aardvark") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author") self.assertOperationAttributes(changes, 'testapp', 0, 2, name="Author") + + def test_fk_dependency_other_app(self): + """ + Tests that ForeignKeys correctly depend on other apps' models (#23100) + """ + # Make state + before = self.make_project_state([self.author_name, self.book]) + after = self.make_project_state([self.author_with_book, self.book]) + autodetector = MigrationAutodetector(before, after) + changes = autodetector._detect_changes() + # Right number of migrations? + self.assertNumberMigrations(changes, 'testapp', 1) + self.assertOperationTypes(changes, 'testapp', 0, ["AddField"]) + self.assertOperationAttributes(changes, 'testapp', 0, 0, name="book") + self.assertEqual(changes['testapp'][0].dependencies, [("otherapp", "__first__")])