Refs #27064 -- Made migrations generate RenameIndex operations when moving indexes from index_together to Meta.indexes.
This commit is contained in:
parent
a098cde968
commit
97f124f39e
|
@ -1,5 +1,6 @@
|
||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -1213,6 +1214,8 @@ class MigrationAutodetector:
|
||||||
|
|
||||||
def create_altered_indexes(self):
|
def create_altered_indexes(self):
|
||||||
option_name = operations.AddIndex.option_name
|
option_name = operations.AddIndex.option_name
|
||||||
|
self.renamed_index_together_values = defaultdict(list)
|
||||||
|
|
||||||
for app_label, model_name in sorted(self.kept_model_keys):
|
for app_label, model_name in sorted(self.kept_model_keys):
|
||||||
old_model_name = self.renamed_models.get(
|
old_model_name = self.renamed_models.get(
|
||||||
(app_label, model_name), model_name
|
(app_label, model_name), model_name
|
||||||
|
@ -1242,6 +1245,43 @@ class MigrationAutodetector:
|
||||||
renamed_indexes.append((old_index_name, new_index_name, None))
|
renamed_indexes.append((old_index_name, new_index_name, None))
|
||||||
remove_from_added.append(new_index)
|
remove_from_added.append(new_index)
|
||||||
remove_from_removed.append(old_index)
|
remove_from_removed.append(old_index)
|
||||||
|
# Find index_together changed to indexes.
|
||||||
|
for (
|
||||||
|
old_value,
|
||||||
|
new_value,
|
||||||
|
index_together_app_label,
|
||||||
|
index_together_model_name,
|
||||||
|
dependencies,
|
||||||
|
) in self._get_altered_foo_together_operations(
|
||||||
|
operations.AlterIndexTogether.option_name
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
app_label != index_together_app_label
|
||||||
|
or model_name != index_together_model_name
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
removed_values = old_value.difference(new_value)
|
||||||
|
for removed_index_together in removed_values:
|
||||||
|
renamed_index_together_indexes = []
|
||||||
|
for new_index in added_indexes:
|
||||||
|
_, args, kwargs = new_index.deconstruct()
|
||||||
|
# Ensure only 'fields' are defined in the Index.
|
||||||
|
if (
|
||||||
|
not args
|
||||||
|
and new_index.fields == list(removed_index_together)
|
||||||
|
and set(kwargs) == {"name", "fields"}
|
||||||
|
):
|
||||||
|
renamed_index_together_indexes.append(new_index)
|
||||||
|
|
||||||
|
if len(renamed_index_together_indexes) == 1:
|
||||||
|
renamed_index = renamed_index_together_indexes[0]
|
||||||
|
remove_from_added.append(renamed_index)
|
||||||
|
renamed_indexes.append(
|
||||||
|
(None, renamed_index.name, removed_index_together)
|
||||||
|
)
|
||||||
|
self.renamed_index_together_values[
|
||||||
|
index_together_app_label, index_together_model_name
|
||||||
|
].append(removed_index_together)
|
||||||
# Remove renamed indexes from the lists of added and removed
|
# Remove renamed indexes from the lists of added and removed
|
||||||
# indexes.
|
# indexes.
|
||||||
added_indexes = [
|
added_indexes = [
|
||||||
|
@ -1439,6 +1479,13 @@ class MigrationAutodetector:
|
||||||
model_name,
|
model_name,
|
||||||
dependencies,
|
dependencies,
|
||||||
) in self._get_altered_foo_together_operations(operation.option_name):
|
) in self._get_altered_foo_together_operations(operation.option_name):
|
||||||
|
if operation == operations.AlterIndexTogether:
|
||||||
|
old_value = {
|
||||||
|
value
|
||||||
|
for value in old_value
|
||||||
|
if value
|
||||||
|
not in self.renamed_index_together_values[app_label, model_name]
|
||||||
|
}
|
||||||
removal_value = new_value.intersection(old_value)
|
removal_value = new_value.intersection(old_value)
|
||||||
if removal_value or old_value:
|
if removal_value or old_value:
|
||||||
self.add_operation(
|
self.add_operation(
|
||||||
|
|
|
@ -355,6 +355,12 @@ Migrations
|
||||||
``RemoveIndex`` and ``AddIndex``, when renaming indexes defined in the
|
``RemoveIndex`` and ``AddIndex``, when renaming indexes defined in the
|
||||||
:attr:`Meta.indexes <django.db.models.Options.indexes>`.
|
:attr:`Meta.indexes <django.db.models.Options.indexes>`.
|
||||||
|
|
||||||
|
* The migrations autodetector now generates
|
||||||
|
:class:`~django.db.migrations.operations.RenameIndex` operations instead of
|
||||||
|
``AlterIndexTogether`` and ``AddIndex``, when moving indexes defined in the
|
||||||
|
:attr:`Meta.index_together <django.db.models.Options.index_together>` to the
|
||||||
|
:attr:`Meta.indexes <django.db.models.Options.indexes>`.
|
||||||
|
|
||||||
Models
|
Models
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -2598,6 +2598,79 @@ class AutodetectorTests(TestCase):
|
||||||
old_name="book_title_author_idx",
|
old_name="book_title_author_idx",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_rename_index_together_to_index(self):
|
||||||
|
changes = self.get_changes(
|
||||||
|
[self.author_empty, self.book_foo_together],
|
||||||
|
[self.author_empty, self.book_indexes],
|
||||||
|
)
|
||||||
|
self.assertNumberMigrations(changes, "otherapp", 1)
|
||||||
|
self.assertOperationTypes(
|
||||||
|
changes, "otherapp", 0, ["RenameIndex", "AlterUniqueTogether"]
|
||||||
|
)
|
||||||
|
self.assertOperationAttributes(
|
||||||
|
changes,
|
||||||
|
"otherapp",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
model_name="book",
|
||||||
|
new_name="book_title_author_idx",
|
||||||
|
old_fields=("author", "title"),
|
||||||
|
)
|
||||||
|
self.assertOperationAttributes(
|
||||||
|
changes,
|
||||||
|
"otherapp",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
name="book",
|
||||||
|
unique_together=set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rename_index_together_to_index_extra_options(self):
|
||||||
|
# Indexes with extra options don't match indexes in index_together.
|
||||||
|
book_partial_index = ModelState(
|
||||||
|
"otherapp",
|
||||||
|
"Book",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("author", models.ForeignKey("testapp.Author", models.CASCADE)),
|
||||||
|
("title", models.CharField(max_length=200)),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"indexes": [
|
||||||
|
models.Index(
|
||||||
|
fields=["author", "title"],
|
||||||
|
condition=models.Q(title__startswith="The"),
|
||||||
|
name="book_title_author_idx",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
changes = self.get_changes(
|
||||||
|
[self.author_empty, self.book_foo_together],
|
||||||
|
[self.author_empty, book_partial_index],
|
||||||
|
)
|
||||||
|
self.assertNumberMigrations(changes, "otherapp", 1)
|
||||||
|
self.assertOperationTypes(
|
||||||
|
changes,
|
||||||
|
"otherapp",
|
||||||
|
0,
|
||||||
|
["AlterUniqueTogether", "AlterIndexTogether", "AddIndex"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rename_index_together_to_index_order_fields(self):
|
||||||
|
# Indexes with reordered fields don't match indexes in index_together.
|
||||||
|
changes = self.get_changes(
|
||||||
|
[self.author_empty, self.book_foo_together],
|
||||||
|
[self.author_empty, self.book_unordered_indexes],
|
||||||
|
)
|
||||||
|
self.assertNumberMigrations(changes, "otherapp", 1)
|
||||||
|
self.assertOperationTypes(
|
||||||
|
changes,
|
||||||
|
"otherapp",
|
||||||
|
0,
|
||||||
|
["AlterUniqueTogether", "AlterIndexTogether", "AddIndex"],
|
||||||
|
)
|
||||||
|
|
||||||
def test_order_fields_indexes(self):
|
def test_order_fields_indexes(self):
|
||||||
"""Test change detection of reordering of fields in indexes."""
|
"""Test change detection of reordering of fields in indexes."""
|
||||||
changes = self.get_changes(
|
changes = self.get_changes(
|
||||||
|
|
Loading…
Reference in New Issue