Fixed #25551 -- Fixed migration operations ordering when adding fields and a unique_together constraint.
This commit is contained in:
parent
8092745593
commit
73a6ab6382
|
@ -558,22 +558,7 @@ class MigrationAutodetector(object):
|
||||||
|
|
||||||
# Generate operations for each related field
|
# Generate operations for each related field
|
||||||
for name, field in sorted(related_fields.items()):
|
for name, field in sorted(related_fields.items()):
|
||||||
# Account for FKs to swappable models
|
dependencies = self._get_dependecies_for_foreign_key(field)
|
||||||
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.remote_field.model._meta.app_label
|
|
||||||
dep_object_name = field.remote_field.model._meta.object_name
|
|
||||||
dependencies = [(dep_app_label, dep_object_name, None, True)]
|
|
||||||
if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
|
|
||||||
dependencies.append((
|
|
||||||
field.remote_field.through._meta.app_label,
|
|
||||||
field.remote_field.through._meta.object_name,
|
|
||||||
None,
|
|
||||||
True
|
|
||||||
))
|
|
||||||
# Depend on our own model being created
|
# Depend on our own model being created
|
||||||
dependencies.append((app_label, model_name, None, True))
|
dependencies.append((app_label, model_name, None, True))
|
||||||
# Make operation
|
# Make operation
|
||||||
|
@ -810,22 +795,7 @@ class MigrationAutodetector(object):
|
||||||
# Fields that are foreignkeys/m2ms depend on stuff
|
# Fields that are foreignkeys/m2ms depend on stuff
|
||||||
dependencies = []
|
dependencies = []
|
||||||
if field.remote_field and field.remote_field.model:
|
if field.remote_field and field.remote_field.model:
|
||||||
# Account for FKs to swappable models
|
dependencies.extend(self._get_dependecies_for_foreign_key(field))
|
||||||
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.remote_field.model._meta.app_label
|
|
||||||
dep_object_name = field.remote_field.model._meta.object_name
|
|
||||||
dependencies = [(dep_app_label, dep_object_name, None, True)]
|
|
||||||
if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
|
|
||||||
dependencies.append((
|
|
||||||
field.remote_field.through._meta.app_label,
|
|
||||||
field.remote_field.through._meta.object_name,
|
|
||||||
None,
|
|
||||||
True,
|
|
||||||
))
|
|
||||||
# You can't just add NOT NULL fields with no default or fields
|
# You can't just add NOT NULL fields with no default or fields
|
||||||
# which don't allow empty strings as default.
|
# which don't allow empty strings as default.
|
||||||
preserve_default = True
|
preserve_default = True
|
||||||
|
@ -925,6 +895,25 @@ class MigrationAutodetector(object):
|
||||||
self._generate_removed_field(app_label, model_name, field_name)
|
self._generate_removed_field(app_label, model_name, field_name)
|
||||||
self._generate_added_field(app_label, model_name, field_name)
|
self._generate_added_field(app_label, model_name, field_name)
|
||||||
|
|
||||||
|
def _get_dependecies_for_foreign_key(self, field):
|
||||||
|
# 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.remote_field.model._meta.app_label
|
||||||
|
dep_object_name = field.remote_field.model._meta.object_name
|
||||||
|
dependencies = [(dep_app_label, dep_object_name, None, True)]
|
||||||
|
if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
|
||||||
|
dependencies.append((
|
||||||
|
field.remote_field.through._meta.app_label,
|
||||||
|
field.remote_field.through._meta.object_name,
|
||||||
|
None,
|
||||||
|
True,
|
||||||
|
))
|
||||||
|
return dependencies
|
||||||
|
|
||||||
def _generate_altered_foo_together(self, operation):
|
def _generate_altered_foo_together(self, operation):
|
||||||
option_name = operation.option_name
|
option_name = operation.option_name
|
||||||
for app_label, model_name in sorted(self.kept_model_keys):
|
for app_label, model_name in sorted(self.kept_model_keys):
|
||||||
|
@ -948,12 +937,20 @@ class MigrationAutodetector(object):
|
||||||
new_value = set(new_value)
|
new_value = set(new_value)
|
||||||
|
|
||||||
if old_value != new_value:
|
if old_value != new_value:
|
||||||
|
dependencies = []
|
||||||
|
for foo_togethers in new_value:
|
||||||
|
for field_name in foo_togethers:
|
||||||
|
field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name)
|
||||||
|
if field.remote_field and field.remote_field.model:
|
||||||
|
dependencies.extend(self._get_dependecies_for_foreign_key(field))
|
||||||
|
|
||||||
self.add_operation(
|
self.add_operation(
|
||||||
app_label,
|
app_label,
|
||||||
operation(
|
operation(
|
||||||
name=model_name,
|
name=model_name,
|
||||||
**{option_name: new_value}
|
**{option_name: new_value}
|
||||||
)
|
),
|
||||||
|
dependencies=dependencies,
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_altered_unique_together(self):
|
def generate_altered_unique_together(self):
|
||||||
|
|
|
@ -1174,6 +1174,34 @@ class AutodetectorTests(TestCase):
|
||||||
self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={("title", "newfield")})
|
self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={("title", "newfield")})
|
||||||
self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield")})
|
self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield")})
|
||||||
|
|
||||||
|
def test_create_model_and_unique_together(self):
|
||||||
|
author = ModelState("otherapp", "Author", [
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("name", models.CharField(max_length=200)),
|
||||||
|
])
|
||||||
|
book_with_author = ModelState("otherapp", "Book", [
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("author", models.ForeignKey("otherapp.Author", models.CASCADE)),
|
||||||
|
("title", models.CharField(max_length=200)),
|
||||||
|
], {
|
||||||
|
"index_together": {("title", "author")},
|
||||||
|
"unique_together": {("title", "author")},
|
||||||
|
})
|
||||||
|
before = self.make_project_state([self.book_with_no_author])
|
||||||
|
after = self.make_project_state([author, book_with_author])
|
||||||
|
autodetector = MigrationAutodetector(before, after)
|
||||||
|
changes = autodetector._detect_changes()
|
||||||
|
# Right number of migrations?
|
||||||
|
self.assertEqual(len(changes['otherapp']), 1)
|
||||||
|
# Right number of actions?
|
||||||
|
migration = changes['otherapp'][0]
|
||||||
|
self.assertEqual(len(migration.operations), 4)
|
||||||
|
# Right actions order?
|
||||||
|
self.assertOperationTypes(
|
||||||
|
changes, 'otherapp', 0,
|
||||||
|
['CreateModel', 'AddField', 'AlterUniqueTogether', 'AlterIndexTogether']
|
||||||
|
)
|
||||||
|
|
||||||
def test_remove_field_and_foo_together(self):
|
def test_remove_field_and_foo_together(self):
|
||||||
"""
|
"""
|
||||||
Tests that removed fields will be removed after updating
|
Tests that removed fields will be removed after updating
|
||||||
|
|
Loading…
Reference in New Issue