summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Fursov <geyser85@gmail.com>2021-12-29 00:18:17 +0300
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-09-13 10:38:57 +0200
commitb731e8841558ee4caaba766c83f34ea9c7004f8b (patch)
treefeb5596cfa8c5397ad3172f19c2985ecb4c41d19
parent1b08e9bf7d88dbad9324f462e5fc7ec582aef3cc (diff)
Fixed #31335 -- Fixed removing composed composed Meta constraints/indexes on foreign keys on MySQL.
-rw-r--r--django/db/backends/mysql/schema.py55
-rw-r--r--tests/schema/tests.py25
2 files changed, 73 insertions, 7 deletions
diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py
index 044a752c7a..821f4ddbce 100644
--- a/django/db/backends/mysql/schema.py
+++ b/django/db/backends/mysql/schema.py
@@ -1,5 +1,6 @@
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
-from django.db.models import NOT_PROVIDED
+from django.db.models import NOT_PROVIDED, F, UniqueConstraint
+from django.db.models.constants import LOOKUP_SEP
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
@@ -117,6 +118,23 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
[effective_default],
)
+ def remove_constraint(self, model, constraint):
+ if isinstance(constraint, UniqueConstraint):
+ self._create_missing_fk_index(
+ model,
+ fields=constraint.fields,
+ expressions=constraint.expressions,
+ )
+ super().remove_constraint(model, constraint)
+
+ def remove_index(self, model, index):
+ self._create_missing_fk_index(
+ model,
+ fields=[field_name for field_name, _ in index.fields_orders],
+ expressions=index.expressions,
+ )
+ super().remove_index(model, index)
+
def _field_should_be_indexed(self, model, field):
if not super()._field_should_be_indexed(model, field):
return False
@@ -140,6 +158,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
model,
*,
fields,
+ expressions=None,
):
"""
MySQL can remove an implicit FK index on a field when that field is
@@ -150,14 +169,36 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
Manually create an implicit FK index to make it possible to remove the
composed index.
"""
- first_field = model._meta.get_field(fields[0])
+ first_field_name = None
+ if fields:
+ first_field_name = fields[0]
+ elif (
+ expressions
+ and self.connection.features.supports_expression_indexes
+ and isinstance(expressions[0], F)
+ and LOOKUP_SEP not in expressions[0].name
+ ):
+ first_field_name = expressions[0].name
+
+ if not first_field_name:
+ return
+
+ first_field = model._meta.get_field(first_field_name)
if first_field.get_internal_type() == "ForeignKey":
- constraint_names = self._constraint_names(
- model,
- [first_field.column],
- index=True,
+ column = self.connection.introspection.identifier_converter(
+ first_field.column
)
- if not constraint_names:
+ with self.connection.cursor() as cursor:
+ constraint_names = [
+ name
+ for name, infodict in self.connection.introspection.get_constraints(
+ cursor, model._meta.db_table
+ ).items()
+ if infodict["index"] and infodict["columns"][0] == column
+ ]
+ # There are no other indexes that starts with the FK field, only
+ # the index that is expected to be deleted.
+ if len(constraint_names) == 1:
self.execute(
self._create_index_sql(model, fields=[first_field], suffix="")
)
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 941b28900e..aaf6dff2ff 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -2735,6 +2735,24 @@ class SchemaTests(TransactionTestCase):
editor.remove_index(Book, index)
self.assertNotIn(index.name, self.get_constraints(table))
+ def test_composed_index_with_fk(self):
+ index = Index(fields=["author", "title"], name="book_author_title_idx")
+ self._test_composed_index_with_fk(index)
+
+ def test_composed_desc_index_with_fk(self):
+ index = Index(fields=["-author", "title"], name="book_author_title_idx")
+ self._test_composed_index_with_fk(index)
+
+ @skipUnlessDBFeature("supports_expression_indexes")
+ def test_composed_func_index_with_fk(self):
+ index = Index(F("author"), F("title"), name="book_author_title_idx")
+ self._test_composed_index_with_fk(index)
+
+ @skipUnlessDBFeature("supports_expression_indexes")
+ def test_composed_desc_func_index_with_fk(self):
+ index = Index(F("author").desc(), F("title"), name="book_author_title_idx")
+ self._test_composed_index_with_fk(index)
+
@skipUnlessDBFeature("supports_expression_indexes")
def test_composed_func_transform_index_with_fk(self):
index = Index(F("title__lower"), name="book_title_lower_idx")
@@ -2756,6 +2774,13 @@ class SchemaTests(TransactionTestCase):
editor.remove_constraint(Book, constraint)
self.assertNotIn(constraint.name, self.get_constraints(table))
+ def test_composed_constraint_with_fk(self):
+ constraint = UniqueConstraint(
+ fields=["author", "title"],
+ name="book_author_title_uniq",
+ )
+ self._test_composed_constraint_with_fk(constraint)
+
@skipUnlessDBFeature(
"supports_column_check_constraints", "can_introspect_check_constraints"
)