mirror of
https://github.com/django/django.git
synced 2025-03-12 10:22:37 +00:00
Fixed #27731 -- Implemented CreateModel/AlterFooOperation reduction.
This should alleviate the side effects of disabling the AlterFooOperation reduction with RemoveField to fix refs #28862 during migration squashing because CreateModel can perform a reduction with RemoveField. Thanks Nick Pope for the review.
This commit is contained in:
parent
ed7898e1b5
commit
8e3f22f251
@ -142,6 +142,17 @@ class CreateModel(ModelOperation):
|
|||||||
managers=self.managers,
|
managers=self.managers,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
elif isinstance(operation, FieldRelatedOptionOperation) and self.name_lower == operation.name_lower:
|
||||||
|
option_value = getattr(operation, operation.option_name)
|
||||||
|
return [
|
||||||
|
CreateModel(
|
||||||
|
self.name,
|
||||||
|
fields=self.fields,
|
||||||
|
options={**self.options, **{operation.option_name: option_value}},
|
||||||
|
bases=self.bases,
|
||||||
|
managers=self.managers,
|
||||||
|
),
|
||||||
|
]
|
||||||
elif isinstance(operation, FieldOperation) and self.name_lower == operation.model_name_lower:
|
elif isinstance(operation, FieldOperation) and self.name_lower == operation.model_name_lower:
|
||||||
if isinstance(operation, AddField):
|
if isinstance(operation, AddField):
|
||||||
return [
|
return [
|
||||||
@ -167,6 +178,18 @@ class CreateModel(ModelOperation):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
elif isinstance(operation, RemoveField):
|
elif isinstance(operation, RemoveField):
|
||||||
|
options = self.options.copy()
|
||||||
|
for option_name in ('unique_together', 'index_together'):
|
||||||
|
option = options.pop(option_name, None)
|
||||||
|
if option:
|
||||||
|
option = set(filter(bool, (
|
||||||
|
tuple(f for f in fields if f != operation.name_lower) for fields in option
|
||||||
|
)))
|
||||||
|
if option:
|
||||||
|
options[option_name] = option
|
||||||
|
order_with_respect_to = options.get('order_with_respect_to')
|
||||||
|
if order_with_respect_to == operation.name_lower:
|
||||||
|
del options['order_with_respect_to']
|
||||||
return [
|
return [
|
||||||
CreateModel(
|
CreateModel(
|
||||||
self.name,
|
self.name,
|
||||||
@ -175,12 +198,23 @@ class CreateModel(ModelOperation):
|
|||||||
for n, v in self.fields
|
for n, v in self.fields
|
||||||
if n.lower() != operation.name_lower
|
if n.lower() != operation.name_lower
|
||||||
],
|
],
|
||||||
options=self.options,
|
options=options,
|
||||||
bases=self.bases,
|
bases=self.bases,
|
||||||
managers=self.managers,
|
managers=self.managers,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
elif isinstance(operation, RenameField):
|
elif isinstance(operation, RenameField):
|
||||||
|
options = self.options.copy()
|
||||||
|
for option_name in ('unique_together', 'index_together'):
|
||||||
|
option = options.get(option_name)
|
||||||
|
if option:
|
||||||
|
options[option_name] = {
|
||||||
|
tuple(operation.new_name if f == operation.old_name else f for f in fields)
|
||||||
|
for fields in option
|
||||||
|
}
|
||||||
|
order_with_respect_to = options.get('order_with_respect_to')
|
||||||
|
if order_with_respect_to == operation.old_name:
|
||||||
|
options['order_with_respect_to'] = operation.new_name
|
||||||
return [
|
return [
|
||||||
CreateModel(
|
CreateModel(
|
||||||
self.name,
|
self.name,
|
||||||
@ -188,7 +222,7 @@ class CreateModel(ModelOperation):
|
|||||||
(operation.new_name if n == operation.old_name else n, v)
|
(operation.new_name if n == operation.old_name else n, v)
|
||||||
for n, v in self.fields
|
for n, v in self.fields
|
||||||
],
|
],
|
||||||
options=self.options,
|
options=options,
|
||||||
bases=self.bases,
|
bases=self.bases,
|
||||||
managers=self.managers,
|
managers=self.managers,
|
||||||
),
|
),
|
||||||
@ -568,6 +602,8 @@ class AlterIndexTogether(FieldRelatedOptionOperation):
|
|||||||
class AlterOrderWithRespectTo(FieldRelatedOptionOperation):
|
class AlterOrderWithRespectTo(FieldRelatedOptionOperation):
|
||||||
"""Represent a change with the order_with_respect_to option."""
|
"""Represent a change with the order_with_respect_to option."""
|
||||||
|
|
||||||
|
option_name = 'order_with_respect_to'
|
||||||
|
|
||||||
def __init__(self, name, order_with_respect_to):
|
def __init__(self, name, order_with_respect_to):
|
||||||
self.order_with_respect_to = order_with_respect_to
|
self.order_with_respect_to = order_with_respect_to
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
@ -2075,8 +2075,10 @@ class AutodetectorTests(TestCase):
|
|||||||
changes = self.get_changes([], [self.book, self.author_with_book_order_wrt])
|
changes = self.get_changes([], [self.book, self.author_with_book_order_wrt])
|
||||||
# Right number/type of migrations?
|
# Right number/type of migrations?
|
||||||
self.assertNumberMigrations(changes, 'testapp', 1)
|
self.assertNumberMigrations(changes, 'testapp', 1)
|
||||||
self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "AlterOrderWithRespectTo"])
|
self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])
|
||||||
self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author", order_with_respect_to="book")
|
self.assertOperationAttributes(
|
||||||
|
changes, 'testapp', 0, 0, name="Author", options={'order_with_respect_to': 'book'}
|
||||||
|
)
|
||||||
self.assertNotIn("_order", [name for name, field in changes['testapp'][0].operations[0].fields])
|
self.assertNotIn("_order", [name for name, field in changes['testapp'][0].operations[0].fields])
|
||||||
|
|
||||||
def test_alter_model_managers(self):
|
def test_alter_model_managers(self):
|
||||||
|
@ -1335,7 +1335,7 @@ class SquashMigrationsTests(MigrationTestBase):
|
|||||||
out = io.StringIO()
|
out = io.StringIO()
|
||||||
with self.temporary_migration_module(module="migrations.test_migrations"):
|
with self.temporary_migration_module(module="migrations.test_migrations"):
|
||||||
call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=1, stdout=out)
|
call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=1, stdout=out)
|
||||||
self.assertIn("Optimized from 8 operations to 4 operations.", out.getvalue())
|
self.assertIn("Optimized from 8 operations to 2 operations.", out.getvalue())
|
||||||
|
|
||||||
def test_ticket_23799_squashmigrations_no_optimize(self):
|
def test_ticket_23799_squashmigrations_no_optimize(self):
|
||||||
"""
|
"""
|
||||||
|
@ -594,8 +594,11 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
def _test_create_alter_foo_field(self, alter):
|
def _test_create_alter_foo_field(self, alter):
|
||||||
"""
|
"""
|
||||||
CreateModel, AlterFooTogether/AlterOrderWithRespectTo followed by an
|
CreateModel, AlterFooTogether/AlterOrderWithRespectTo followed by an
|
||||||
add/alter/rename field should optimize to CreateModel and the Alter*
|
add/alter/rename field should optimize to CreateModel with options.
|
||||||
"""
|
"""
|
||||||
|
option_value = getattr(alter, alter.option_name)
|
||||||
|
options = {alter.option_name: option_value}
|
||||||
|
|
||||||
# AddField
|
# AddField
|
||||||
self.assertOptimizesTo(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
@ -611,13 +614,12 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
("b", models.IntegerField()),
|
("b", models.IntegerField()),
|
||||||
("c", models.IntegerField()),
|
("c", models.IntegerField()),
|
||||||
]),
|
], options=options),
|
||||||
alter,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# AlterField
|
# AlterField
|
||||||
self.assertDoesNotOptimize(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
migrations.CreateModel("Foo", [
|
migrations.CreateModel("Foo", [
|
||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
@ -626,6 +628,12 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
alter,
|
alter,
|
||||||
migrations.AlterField("Foo", "b", models.CharField(max_length=255)),
|
migrations.AlterField("Foo", "b", models.CharField(max_length=255)),
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
migrations.CreateModel("Foo", [
|
||||||
|
("a", models.IntegerField()),
|
||||||
|
("b", models.CharField(max_length=255)),
|
||||||
|
], options=options),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertOptimizesTo(
|
self.assertOptimizesTo(
|
||||||
@ -643,13 +651,20 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
("b", models.IntegerField()),
|
("b", models.IntegerField()),
|
||||||
("c", models.CharField(max_length=255)),
|
("c", models.CharField(max_length=255)),
|
||||||
]),
|
], options=options),
|
||||||
alter,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# RenameField
|
# RenameField
|
||||||
self.assertDoesNotOptimize(
|
if isinstance(option_value, str):
|
||||||
|
renamed_options = {alter.option_name: 'c'}
|
||||||
|
else:
|
||||||
|
renamed_options = {
|
||||||
|
alter.option_name: {
|
||||||
|
tuple('c' if value == 'b' else value for value in item) for item in option_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
migrations.CreateModel("Foo", [
|
migrations.CreateModel("Foo", [
|
||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
@ -658,6 +673,12 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
alter,
|
alter,
|
||||||
migrations.RenameField("Foo", "b", "c"),
|
migrations.RenameField("Foo", "b", "c"),
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
migrations.CreateModel("Foo", [
|
||||||
|
("a", models.IntegerField()),
|
||||||
|
("c", models.IntegerField()),
|
||||||
|
], options=renamed_options),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertOptimizesTo(
|
self.assertOptimizesTo(
|
||||||
@ -673,10 +694,8 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
[
|
[
|
||||||
migrations.CreateModel("Foo", [
|
migrations.CreateModel("Foo", [
|
||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
("b", models.IntegerField()),
|
("c", models.IntegerField()),
|
||||||
]),
|
], options=renamed_options),
|
||||||
alter,
|
|
||||||
migrations.RenameField("Foo", "b", "c"),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -695,13 +714,20 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
("b", models.IntegerField()),
|
("b", models.IntegerField()),
|
||||||
("d", models.IntegerField()),
|
("d", models.IntegerField()),
|
||||||
]),
|
], options=options),
|
||||||
alter,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# RemoveField
|
# RemoveField
|
||||||
self.assertDoesNotOptimize(
|
if isinstance(option_value, str):
|
||||||
|
removed_options = None
|
||||||
|
else:
|
||||||
|
removed_options = {
|
||||||
|
alter.option_name: {
|
||||||
|
tuple(value for value in item if value != 'b') for item in option_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
migrations.CreateModel("Foo", [
|
migrations.CreateModel("Foo", [
|
||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
@ -710,9 +736,14 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
alter,
|
alter,
|
||||||
migrations.RemoveField("Foo", "b"),
|
migrations.RemoveField("Foo", "b"),
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
migrations.CreateModel("Foo", [
|
||||||
|
("a", models.IntegerField()),
|
||||||
|
], options=removed_options),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertDoesNotOptimize(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
migrations.CreateModel("Foo", [
|
migrations.CreateModel("Foo", [
|
||||||
("a", models.IntegerField()),
|
("a", models.IntegerField()),
|
||||||
@ -722,6 +753,12 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
alter,
|
alter,
|
||||||
migrations.RemoveField("Foo", "c"),
|
migrations.RemoveField("Foo", "c"),
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
migrations.CreateModel("Foo", [
|
||||||
|
("a", models.IntegerField()),
|
||||||
|
("b", models.IntegerField()),
|
||||||
|
], options=options),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_alter_unique_field(self):
|
def test_create_alter_unique_field(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user