summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wobrock <david.wobrock@gmail.com>2022-05-03 14:43:06 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-05-16 17:46:24 +0200
commitc6cec3c2d287b5d2bd36d9c3002712ae89b5ab17 (patch)
treefb370652afdf7d742e39447324ed22f38e87eb58
parent11310e9abbdd784aea4ba50451144fc9c239f71f (diff)
Refs #27064 -- Made migrations generate RenameIndex operations when renaming Meta.indexes.
-rw-r--r--django/db/migrations/autodetector.py51
-rw-r--r--docs/releases/4.1.txt5
-rw-r--r--tests/migrations/test_autodetector.py33
3 files changed, 85 insertions, 4 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 04e651ec4a..0616ea4dbe 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -179,6 +179,7 @@ class MigrationAutodetector:
self.generate_removed_indexes()
# Generate field renaming operations.
self.generate_renamed_fields()
+ self.generate_renamed_indexes()
# Generate removal of foo together.
self.generate_removed_altered_unique_together()
self.generate_removed_altered_index_together()
@@ -1187,14 +1188,41 @@ class MigrationAutodetector:
old_indexes = old_model_state.options[option_name]
new_indexes = new_model_state.options[option_name]
- add_idx = [idx for idx in new_indexes if idx not in old_indexes]
- rem_idx = [idx for idx in old_indexes if idx not in new_indexes]
+ added_indexes = [idx for idx in new_indexes if idx not in old_indexes]
+ removed_indexes = [idx for idx in old_indexes if idx not in new_indexes]
+ renamed_indexes = []
+ # Find renamed indexes.
+ remove_from_added = []
+ remove_from_removed = []
+ for new_index in added_indexes:
+ new_index_dec = new_index.deconstruct()
+ new_index_name = new_index_dec[2].pop("name")
+ for old_index in removed_indexes:
+ old_index_dec = old_index.deconstruct()
+ old_index_name = old_index_dec[2].pop("name")
+ # Indexes are the same except for the names.
+ if (
+ new_index_dec == old_index_dec
+ and new_index_name != old_index_name
+ ):
+ renamed_indexes.append((old_index_name, new_index_name, None))
+ remove_from_added.append(new_index)
+ remove_from_removed.append(old_index)
+ # Remove renamed indexes from the lists of added and removed
+ # indexes.
+ added_indexes = [
+ idx for idx in added_indexes if idx not in remove_from_added
+ ]
+ removed_indexes = [
+ idx for idx in removed_indexes if idx not in remove_from_removed
+ ]
self.altered_indexes.update(
{
(app_label, model_name): {
- "added_indexes": add_idx,
- "removed_indexes": rem_idx,
+ "added_indexes": added_indexes,
+ "removed_indexes": removed_indexes,
+ "renamed_indexes": renamed_indexes,
}
}
)
@@ -1221,6 +1249,21 @@ class MigrationAutodetector:
),
)
+ def generate_renamed_indexes(self):
+ for (app_label, model_name), alt_indexes in self.altered_indexes.items():
+ for old_index_name, new_index_name, old_fields in alt_indexes[
+ "renamed_indexes"
+ ]:
+ self.add_operation(
+ app_label,
+ operations.RenameIndex(
+ model_name=model_name,
+ new_name=new_index_name,
+ old_name=old_index_name,
+ old_fields=old_fields,
+ ),
+ )
+
def create_altered_constraints(self):
option_name = operations.AddConstraint.option_name
for app_label, model_name in sorted(self.kept_model_keys):
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index dc95a653c3..7c9f1beca6 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -350,6 +350,11 @@ Migrations
:attr:`Meta.indexes <django.db.models.Options.indexes>` or
:attr:`~django.db.models.Options.index_together` options.
+* The migrations autodetector now generates
+ :class:`~django.db.migrations.operations.RenameIndex` operations instead of
+ ``RemoveIndex`` and ``AddIndex``, when renaming indexes defined in 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 0d7a44e42d..a28477e590 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -2565,6 +2565,39 @@ class AutodetectorTests(TestCase):
changes, "otherapp", 0, 0, model_name="book", name="book_title_author_idx"
)
+ def test_rename_indexes(self):
+ book_renamed_indexes = 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"], name="renamed_book_title_author_idx"
+ )
+ ],
+ },
+ )
+ changes = self.get_changes(
+ [self.author_empty, self.book_indexes],
+ [self.author_empty, book_renamed_indexes],
+ )
+ self.assertNumberMigrations(changes, "otherapp", 1)
+ self.assertOperationTypes(changes, "otherapp", 0, ["RenameIndex"])
+ self.assertOperationAttributes(
+ changes,
+ "otherapp",
+ 0,
+ 0,
+ model_name="book",
+ new_name="renamed_book_title_author_idx",
+ old_name="book_title_author_idx",
+ )
+
def test_order_fields_indexes(self):
"""Test change detection of reordering of fields in indexes."""
changes = self.get_changes(