Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 290fd5e

Browse files
committed
[4.2.x] Fixed #34529, Refs #34525 -- Reduced index operations with Meta.indexes/index_together when optimizing migrations.
This makes squashing migrations an available path for changing Meta.index_together, which is deprecated, to Meta.indexes. Follow up to f810325. Backport of 8e2460d from main.
1 parent 4c68482 commit 290fd5e

File tree

5 files changed

+326
-68
lines changed

5 files changed

+326
-68
lines changed

django/db/migrations/operations/models.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,71 @@ def reduce(self, operation, app_label):
303303
managers=self.managers,
304304
),
305305
]
306+
elif (
307+
isinstance(operation, IndexOperation)
308+
and self.name_lower == operation.model_name_lower
309+
):
310+
if isinstance(operation, AddIndex):
311+
return [
312+
CreateModel(
313+
self.name,
314+
fields=self.fields,
315+
options={
316+
**self.options,
317+
"indexes": [
318+
*self.options.get("indexes", []),
319+
operation.index,
320+
],
321+
},
322+
bases=self.bases,
323+
managers=self.managers,
324+
),
325+
]
326+
elif isinstance(operation, RemoveIndex):
327+
options_indexes = [
328+
index
329+
for index in self.options.get("indexes", [])
330+
if index.name != operation.name
331+
]
332+
return [
333+
CreateModel(
334+
self.name,
335+
fields=self.fields,
336+
options={
337+
**self.options,
338+
"indexes": options_indexes,
339+
},
340+
bases=self.bases,
341+
managers=self.managers,
342+
),
343+
]
344+
elif isinstance(operation, RenameIndex) and operation.old_fields:
345+
options_index_together = {
346+
fields
347+
for fields in self.options.get("index_together", [])
348+
if fields != operation.old_fields
349+
}
350+
if options_index_together:
351+
self.options["index_together"] = options_index_together
352+
else:
353+
self.options.pop("index_together", None)
354+
return [
355+
CreateModel(
356+
self.name,
357+
fields=self.fields,
358+
options={
359+
**self.options,
360+
"indexes": [
361+
*self.options.get("indexes", []),
362+
models.Index(
363+
fields=operation.old_fields, name=operation.new_name
364+
),
365+
],
366+
},
367+
bases=self.bases,
368+
managers=self.managers,
369+
),
370+
]
306371
return super().reduce(operation, app_label)
307372

308373

docs/releases/4.2.1.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,8 @@ Bugfixes
5353

5454
* Fixed a regression in Django 4.2 where breadcrumbs didn't appear on admin
5555
site app index views (:ticket:`34512`).
56+
57+
* Made squashing migrations reduce ``AddIndex``, ``RemoveIndex``,
58+
``RenameIndex``, and ``CreateModel`` operations which allows removing a
59+
deprecated ``Meta.index_together`` option from historical migrations and use
60+
``Meta.indexes`` instead (:ticket:`34525`).

docs/releases/4.2.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,8 @@ Should become::
502502

503503
Running the :djadmin:`makemigrations` command will generate a migration
504504
containing a :class:`~django.db.migrations.operations.RenameIndex` operation
505-
which will rename the existing index.
505+
which will rename the existing index. Next, consider squashing migrations to
506+
remove ``index_together`` from historical migrations.
506507

507508
The ``AlterIndexTogether`` migration operation is now officially supported only
508509
for pre-Django 4.2 migration files. For backward compatibility reasons, it's

tests/migrations/test_autodetector.py

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,10 +2266,9 @@ def test_same_app_circular_fk_dependency_with_unique_together_and_indexes(self):
22662266
changes,
22672267
"eggs",
22682268
0,
2269-
["CreateModel", "CreateModel", "AddIndex", "AlterUniqueTogether"],
2269+
["CreateModel", "CreateModel"],
22702270
)
22712271
self.assertNotIn("unique_together", changes["eggs"][0].operations[0].options)
2272-
self.assertNotIn("unique_together", changes["eggs"][0].operations[1].options)
22732272
self.assertMigrationDependencies(changes, "eggs", 0, [])
22742273

22752274
def test_alter_db_table_add(self):
@@ -2565,6 +2564,9 @@ def test(from_state, to_state, msg):
25652564

25662565
def test_create_model_with_indexes(self):
25672566
"""Test creation of new model with indexes already defined."""
2567+
added_index = models.Index(
2568+
fields=["name"], name="create_model_with_indexes_idx"
2569+
)
25682570
author = ModelState(
25692571
"otherapp",
25702572
"Author",
@@ -2573,25 +2575,25 @@ def test_create_model_with_indexes(self):
25732575
("name", models.CharField(max_length=200)),
25742576
],
25752577
{
2576-
"indexes": [
2577-
models.Index(fields=["name"], name="create_model_with_indexes_idx")
2578-
]
2578+
"indexes": [added_index],
25792579
},
25802580
)
25812581
changes = self.get_changes([], [author])
2582-
added_index = models.Index(
2583-
fields=["name"], name="create_model_with_indexes_idx"
2584-
)
25852582
# Right number of migrations?
25862583
self.assertEqual(len(changes["otherapp"]), 1)
25872584
# Right number of actions?
25882585
migration = changes["otherapp"][0]
2589-
self.assertEqual(len(migration.operations), 2)
2586+
self.assertEqual(len(migration.operations), 1)
25902587
# Right actions order?
2591-
self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel", "AddIndex"])
2588+
self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
25922589
self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Author")
25932590
self.assertOperationAttributes(
2594-
changes, "otherapp", 0, 1, model_name="author", index=added_index
2591+
changes,
2592+
"otherapp",
2593+
0,
2594+
0,
2595+
name="Author",
2596+
options={"indexes": [added_index]},
25952597
)
25962598

25972599
def test_add_indexes(self):
@@ -3984,62 +3986,69 @@ def test_add_model_order_with_respect_to_unique_together(self):
39843986
},
39853987
)
39863988

3987-
def test_add_model_order_with_respect_to_index_constraint(self):
3988-
tests = [
3989-
(
3990-
"AddIndex",
3991-
{
3992-
"indexes": [
3993-
models.Index(fields=["_order"], name="book_order_idx"),
3994-
]
3995-
},
3996-
),
3997-
(
3998-
"AddConstraint",
3999-
{
4000-
"constraints": [
4001-
models.CheckConstraint(
4002-
check=models.Q(_order__gt=1),
4003-
name="book_order_gt_1",
4004-
),
4005-
]
4006-
},
4007-
),
4008-
]
4009-
for operation, extra_option in tests:
4010-
with self.subTest(operation=operation):
4011-
after = ModelState(
4012-
"testapp",
4013-
"Author",
4014-
[
4015-
("id", models.AutoField(primary_key=True)),
4016-
("name", models.CharField(max_length=200)),
4017-
("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
4018-
],
4019-
options={
4020-
"order_with_respect_to": "book",
4021-
**extra_option,
4022-
},
4023-
)
4024-
changes = self.get_changes([], [self.book, after])
4025-
self.assertNumberMigrations(changes, "testapp", 1)
4026-
self.assertOperationTypes(
4027-
changes,
4028-
"testapp",
4029-
0,
4030-
[
4031-
"CreateModel",
4032-
operation,
4033-
],
4034-
)
4035-
self.assertOperationAttributes(
4036-
changes,
4037-
"testapp",
4038-
0,
4039-
0,
4040-
name="Author",
4041-
options={"order_with_respect_to": "book"},
4042-
)
3989+
def test_add_model_order_with_respect_to_constraint(self):
3990+
after = ModelState(
3991+
"testapp",
3992+
"Author",
3993+
[
3994+
("id", models.AutoField(primary_key=True)),
3995+
("name", models.CharField(max_length=200)),
3996+
("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
3997+
],
3998+
options={
3999+
"order_with_respect_to": "book",
4000+
"constraints": [
4001+
models.CheckConstraint(
4002+
check=models.Q(_order__gt=1), name="book_order_gt_1"
4003+
),
4004+
],
4005+
},
4006+
)
4007+
changes = self.get_changes([], [self.book, after])
4008+
self.assertNumberMigrations(changes, "testapp", 1)
4009+
self.assertOperationTypes(
4010+
changes,
4011+
"testapp",
4012+
0,
4013+
["CreateModel", "AddConstraint"],
4014+
)
4015+
self.assertOperationAttributes(
4016+
changes,
4017+
"testapp",
4018+
0,
4019+
0,
4020+
name="Author",
4021+
options={"order_with_respect_to": "book"},
4022+
)
4023+
4024+
def test_add_model_order_with_respect_to_index(self):
4025+
after = ModelState(
4026+
"testapp",
4027+
"Author",
4028+
[
4029+
("id", models.AutoField(primary_key=True)),
4030+
("name", models.CharField(max_length=200)),
4031+
("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
4032+
],
4033+
options={
4034+
"order_with_respect_to": "book",
4035+
"indexes": [models.Index(fields=["_order"], name="book_order_idx")],
4036+
},
4037+
)
4038+
changes = self.get_changes([], [self.book, after])
4039+
self.assertNumberMigrations(changes, "testapp", 1)
4040+
self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
4041+
self.assertOperationAttributes(
4042+
changes,
4043+
"testapp",
4044+
0,
4045+
0,
4046+
name="Author",
4047+
options={
4048+
"order_with_respect_to": "book",
4049+
"indexes": [models.Index(fields=["_order"], name="book_order_idx")],
4050+
},
4051+
)
40434052

40444053
def test_set_alter_order_with_respect_to_index_constraint_unique_together(self):
40454054
tests = [

0 commit comments

Comments
 (0)