Fixed #22204: Bad circular-dep-breaking if more than one per run
This commit is contained in:
parent
8ce3ea687c
commit
e46e15e5a1
|
@ -94,9 +94,14 @@ class MigrationAutodetector(object):
|
||||||
# Phase 2 is progressively adding pending models, splitting up into two
|
# Phase 2 is progressively adding pending models, splitting up into two
|
||||||
# migrations if required.
|
# migrations if required.
|
||||||
pending_new_fks = []
|
pending_new_fks = []
|
||||||
|
added_phase_2 = set()
|
||||||
while pending_add:
|
while pending_add:
|
||||||
# Is there one we can add that has all dependencies satisfied?
|
# Is there one we can add that has all dependencies satisfied?
|
||||||
satisfied = [(m, rf) for m, rf in pending_add.items() if all((al, mn) not in pending_add for f, al, mn in rf)]
|
satisfied = [
|
||||||
|
(m, rf)
|
||||||
|
for m, rf in pending_add.items()
|
||||||
|
if all((al, mn) not in pending_add for f, al, mn in rf)
|
||||||
|
]
|
||||||
if satisfied:
|
if satisfied:
|
||||||
(app_label, model_name), related_fields = sorted(satisfied)[0]
|
(app_label, model_name), related_fields = sorted(satisfied)[0]
|
||||||
model_state = self.to_state.models[app_label, model_name]
|
model_state = self.to_state.models[app_label, model_name]
|
||||||
|
@ -107,7 +112,10 @@ class MigrationAutodetector(object):
|
||||||
fields=model_state.fields,
|
fields=model_state.fields,
|
||||||
options=model_state.options,
|
options=model_state.options,
|
||||||
bases=model_state.bases,
|
bases=model_state.bases,
|
||||||
)
|
),
|
||||||
|
# If it's already been added in phase 2 put it in a new
|
||||||
|
# migration for safety.
|
||||||
|
new=any((al, mn) in added_phase_2 for f, al, mn in related_fields),
|
||||||
)
|
)
|
||||||
for field_name, other_app_label, other_model_name in related_fields:
|
for field_name, other_app_label, other_model_name in related_fields:
|
||||||
# If it depends on a swappable something, add a dynamic depend'cy
|
# If it depends on a swappable something, add a dynamic depend'cy
|
||||||
|
@ -117,6 +125,7 @@ class MigrationAutodetector(object):
|
||||||
elif app_label != other_app_label:
|
elif app_label != other_app_label:
|
||||||
self.add_dependency(app_label, other_app_label)
|
self.add_dependency(app_label, other_app_label)
|
||||||
del pending_add[app_label, model_name]
|
del pending_add[app_label, model_name]
|
||||||
|
added_phase_2.add((app_label, model_name))
|
||||||
# Ah well, we'll need to split one. Pick deterministically.
|
# Ah well, we'll need to split one. Pick deterministically.
|
||||||
else:
|
else:
|
||||||
(app_label, model_name), related_fields = sorted(pending_add.items())[0]
|
(app_label, model_name), related_fields = sorted(pending_add.items())[0]
|
||||||
|
@ -342,6 +351,7 @@ class MigrationAutodetector(object):
|
||||||
else:
|
else:
|
||||||
new_name = "%04i_%s" % (next_number, self.suggest_name(migration.operations))
|
new_name = "%04i_%s" % (next_number, self.suggest_name(migration.operations))
|
||||||
name_map[(app_label, migration.name)] = (app_label, new_name)
|
name_map[(app_label, migration.name)] = (app_label, new_name)
|
||||||
|
next_number += 1
|
||||||
migration.name = new_name
|
migration.name = new_name
|
||||||
# Now fix dependencies
|
# Now fix dependencies
|
||||||
for app_label, migrations in changes.items():
|
for app_label, migrations in changes.items():
|
||||||
|
|
|
@ -24,6 +24,7 @@ class AutodetectorTests(TestCase):
|
||||||
author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {}, ("testapp.author", ))
|
author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {}, ("testapp.author", ))
|
||||||
publisher = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100))])
|
publisher = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100))])
|
||||||
publisher_with_author = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100))])
|
publisher_with_author = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100))])
|
||||||
|
publisher_with_book = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("otherapp.Book")), ("name", models.CharField(max_length=100))])
|
||||||
other_pony = ModelState("otherapp", "Pony", [("id", models.AutoField(primary_key=True))])
|
other_pony = ModelState("otherapp", "Pony", [("id", models.AutoField(primary_key=True))])
|
||||||
other_stable = ModelState("otherapp", "Stable", [("id", models.AutoField(primary_key=True))])
|
other_stable = ModelState("otherapp", "Stable", [("id", models.AutoField(primary_key=True))])
|
||||||
third_thing = ModelState("thirdapp", "Thing", [("id", models.AutoField(primary_key=True))])
|
third_thing = ModelState("thirdapp", "Thing", [("id", models.AutoField(primary_key=True))])
|
||||||
|
@ -243,7 +244,7 @@ class AutodetectorTests(TestCase):
|
||||||
"""
|
"""
|
||||||
# Make state
|
# Make state
|
||||||
before = self.make_project_state([])
|
before = self.make_project_state([])
|
||||||
after = self.make_project_state([self.author_with_book, self.book])
|
after = self.make_project_state([self.author_with_book, self.book, self.publisher_with_book])
|
||||||
autodetector = MigrationAutodetector(before, after)
|
autodetector = MigrationAutodetector(before, after)
|
||||||
changes = autodetector._detect_changes()
|
changes = autodetector._detect_changes()
|
||||||
# Right number of migrations?
|
# Right number of migrations?
|
||||||
|
@ -251,7 +252,7 @@ class AutodetectorTests(TestCase):
|
||||||
self.assertEqual(len(changes['otherapp']), 2)
|
self.assertEqual(len(changes['otherapp']), 2)
|
||||||
# Right number of actions?
|
# Right number of actions?
|
||||||
migration1 = changes['testapp'][0]
|
migration1 = changes['testapp'][0]
|
||||||
self.assertEqual(len(migration1.operations), 1)
|
self.assertEqual(len(migration1.operations), 2)
|
||||||
migration2 = changes['otherapp'][0]
|
migration2 = changes['otherapp'][0]
|
||||||
self.assertEqual(len(migration2.operations), 1)
|
self.assertEqual(len(migration2.operations), 1)
|
||||||
migration3 = changes['otherapp'][1]
|
migration3 = changes['otherapp'][1]
|
||||||
|
|
Loading…
Reference in New Issue