Loading django/core/management/commands/makemigrations.py +5 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,11 @@ class Command(BaseCommand): return # Detect changes changes = autodetector.changes(graph=loader.graph, trim_to_apps=app_labels or None) changes = autodetector.changes( graph=loader.graph, trim_to_apps=app_labels or None, convert_apps=app_labels or None, ) # No changes? Tell them. if not changes and self.verbosity >= 1: Loading django/db/migrations/autodetector.py +16 −7 Original line number Diff line number Diff line Loading @@ -28,13 +28,13 @@ class MigrationAutodetector(object): self.to_state = to_state self.questioner = questioner or MigrationQuestioner() def changes(self, graph, trim_to_apps=None): def changes(self, graph, trim_to_apps=None, convert_apps=None): """ Main entry point to produce a list of appliable changes. Takes a graph to base names on and an optional set of apps to try and restrict to (restriction is not guaranteed) """ changes = self._detect_changes() changes = self._detect_changes(convert_apps) changes = self.arrange_for_graph(changes, graph) if trim_to_apps: changes = self._trim_to_apps(changes, trim_to_apps) Loading Loading @@ -77,7 +77,7 @@ class MigrationAutodetector(object): fields_def.append(deconstruction) return fields_def def _detect_changes(self): def _detect_changes(self, convert_apps=None): """ Returns a dict of migration plans which will achieve the change from from_state to to_state. The dict has app labels Loading Loading @@ -105,7 +105,10 @@ class MigrationAutodetector(object): self.new_model_keys = [] for al, mn in sorted(self.to_state.models.keys()): model = self.new_apps.get_model(al, mn) if not model._meta.proxy and model._meta.managed and al not in self.from_state.real_apps: if not model._meta.proxy and model._meta.managed and ( al not in self.from_state.real_apps or (convert_apps and al in convert_apps) ): self.new_model_keys.append((al, mn)) # Renames have to come first Loading Loading @@ -376,6 +379,14 @@ class MigrationAutodetector(object): 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 )) # Make operation self.add_operation( app_label, Loading @@ -384,9 +395,7 @@ class MigrationAutodetector(object): name=name, field=field, ), dependencies=[ (dep_app_label, dep_object_name, None, True), ] dependencies=list(set(dependencies)), ) # Generate other opns if unique_together: Loading tests/migrations/test_autodetector.py +15 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ class AutodetectorTests(TestCase): def assertNumberMigrations(self, changes, app_label, number): if len(changes.get(app_label, [])) != number: self.fail("Incorrect number of migrations (%s) for %s (expected %s)\n%s" % ( len(changes[app_label]), len(changes.get(app_label, [])), app_label, number, self.repr_changes(changes), Loading Loading @@ -706,6 +706,20 @@ class AutodetectorTests(TestCase): self.assertEqual(action.__class__.__name__, "AddField") self.assertEqual(action.name, "publishers") def test_create_with_through_model(self): """ Adding a m2m with a through model and the models that use it should be ordered correctly. """ before = self.make_project_state([]) after = self.make_project_state([self.author_with_m2m_through, self.publisher, self.contract]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertNumberMigrations(changes, "testapp", 1) # Right actions in right order? self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel", "CreateModel", "AddField", "AddField"]) def test_many_to_many_removed_before_through_model(self): """ Removing a ManyToManyField and the "through" model in the same change must remove Loading Loading
django/core/management/commands/makemigrations.py +5 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,11 @@ class Command(BaseCommand): return # Detect changes changes = autodetector.changes(graph=loader.graph, trim_to_apps=app_labels or None) changes = autodetector.changes( graph=loader.graph, trim_to_apps=app_labels or None, convert_apps=app_labels or None, ) # No changes? Tell them. if not changes and self.verbosity >= 1: Loading
django/db/migrations/autodetector.py +16 −7 Original line number Diff line number Diff line Loading @@ -28,13 +28,13 @@ class MigrationAutodetector(object): self.to_state = to_state self.questioner = questioner or MigrationQuestioner() def changes(self, graph, trim_to_apps=None): def changes(self, graph, trim_to_apps=None, convert_apps=None): """ Main entry point to produce a list of appliable changes. Takes a graph to base names on and an optional set of apps to try and restrict to (restriction is not guaranteed) """ changes = self._detect_changes() changes = self._detect_changes(convert_apps) changes = self.arrange_for_graph(changes, graph) if trim_to_apps: changes = self._trim_to_apps(changes, trim_to_apps) Loading Loading @@ -77,7 +77,7 @@ class MigrationAutodetector(object): fields_def.append(deconstruction) return fields_def def _detect_changes(self): def _detect_changes(self, convert_apps=None): """ Returns a dict of migration plans which will achieve the change from from_state to to_state. The dict has app labels Loading Loading @@ -105,7 +105,10 @@ class MigrationAutodetector(object): self.new_model_keys = [] for al, mn in sorted(self.to_state.models.keys()): model = self.new_apps.get_model(al, mn) if not model._meta.proxy and model._meta.managed and al not in self.from_state.real_apps: if not model._meta.proxy and model._meta.managed and ( al not in self.from_state.real_apps or (convert_apps and al in convert_apps) ): self.new_model_keys.append((al, mn)) # Renames have to come first Loading Loading @@ -376,6 +379,14 @@ class MigrationAutodetector(object): 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 )) # Make operation self.add_operation( app_label, Loading @@ -384,9 +395,7 @@ class MigrationAutodetector(object): name=name, field=field, ), dependencies=[ (dep_app_label, dep_object_name, None, True), ] dependencies=list(set(dependencies)), ) # Generate other opns if unique_together: Loading
tests/migrations/test_autodetector.py +15 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ class AutodetectorTests(TestCase): def assertNumberMigrations(self, changes, app_label, number): if len(changes.get(app_label, [])) != number: self.fail("Incorrect number of migrations (%s) for %s (expected %s)\n%s" % ( len(changes[app_label]), len(changes.get(app_label, [])), app_label, number, self.repr_changes(changes), Loading Loading @@ -706,6 +706,20 @@ class AutodetectorTests(TestCase): self.assertEqual(action.__class__.__name__, "AddField") self.assertEqual(action.name, "publishers") def test_create_with_through_model(self): """ Adding a m2m with a through model and the models that use it should be ordered correctly. """ before = self.make_project_state([]) after = self.make_project_state([self.author_with_m2m_through, self.publisher, self.contract]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertNumberMigrations(changes, "testapp", 1) # Right actions in right order? self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel", "CreateModel", "AddField", "AddField"]) def test_many_to_many_removed_before_through_model(self): """ Removing a ManyToManyField and the "through" model in the same change must remove Loading