summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wobrock <david.wobrock@gmail.com>2022-05-04 16:03:09 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-05-17 07:21:36 +0200
commit97f124f39e363bf518fbe428320bdee63d56f8e0 (patch)
treec712856f3477d20403d70a074ef564162bc106f1
parenta098cde9683a560bfc4b22f9b2581cf8cedc7dab (diff)
Refs #27064 -- Made migrations generate RenameIndex operations when moving indexes from index_together to Meta.indexes.
-rw-r--r--django/db/migrations/autodetector.py47
-rw-r--r--docs/releases/4.1.txt6
-rw-r--r--tests/migrations/test_autodetector.py73
3 files changed, 126 insertions, 0 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 00e04fd799..5f41053283 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -1,5 +1,6 @@
import functools
import re
+from collections import defaultdict
from itertools import chain
from django.conf import settings
@@ -1213,6 +1214,8 @@ class MigrationAutodetector:
def create_altered_indexes(self):
option_name = operations.AddIndex.option_name
+ self.renamed_index_together_values = defaultdict(list)
+
for app_label, model_name in sorted(self.kept_model_keys):
old_model_name = self.renamed_models.get(
(app_label, model_name), model_name
@@ -1242,6 +1245,43 @@ class MigrationAutodetector:
renamed_indexes.append((old_index_name, new_index_name, None))
remove_from_added.append(new_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
# indexes.
added_indexes = [
@@ -1439,6 +1479,13 @@ class MigrationAutodetector:
model_name,
dependencies,
) 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)
if removal_value or old_value:
self.add_operation(
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index 7c9f1beca6..bd8af7d51c 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -355,6 +355,12 @@ Migrations
``RemoveIndex`` and ``AddIndex``, when renaming indexes defined in the
: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
~~~~~~
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index a28477e590..547e0b32c5 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -2598,6 +2598,79 @@ class AutodetectorTests(TestCase):
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):
"""Test change detection of reordering of fields in indexes."""
changes = self.get_changes(