summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2014-06-08 17:12:27 -0700
committerAndrew Godwin <andrew@aeracode.org>2014-06-08 17:13:11 -0700
commit55fa4c2d3423b051fce5663b3efc13e458fbb777 (patch)
tree9cd1c6a85959f8a1acee90f71c9ecca1aa23a729
parent25f4e71ed30befbd6af02a7d35807d0d3170e57d (diff)
[1.7.x] Fixed #22777: Add dependency on through for autodetected M2M adds
-rw-r--r--django/core/management/commands/makemigrations.py6
-rw-r--r--django/db/migrations/autodetector.py23
-rw-r--r--tests/migrations/test_autodetector.py16
3 files changed, 36 insertions, 9 deletions
diff --git a/django/core/management/commands/makemigrations.py b/django/core/management/commands/makemigrations.py
index 839592a22a..38ed9db473 100644
--- a/django/core/management/commands/makemigrations.py
+++ b/django/core/management/commands/makemigrations.py
@@ -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:
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 3e3dc242d2..6ed6edc38e 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -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)
@@ -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
@@ -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
@@ -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,
@@ -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:
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index 6d163873ad..f969ae50ac 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -77,7 +77,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),
@@ -694,6 +694,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