Fixed #31335 -- Fixed removing composed composed Meta constraints/indexes on foreign keys on MySQL.
This commit is contained in:
parent
1b08e9bf7d
commit
b731e88415
|
@ -1,5 +1,6 @@
|
||||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
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):
|
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
|
@ -117,6 +118,23 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
[effective_default],
|
[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):
|
def _field_should_be_indexed(self, model, field):
|
||||||
if not super()._field_should_be_indexed(model, field):
|
if not super()._field_should_be_indexed(model, field):
|
||||||
return False
|
return False
|
||||||
|
@ -140,6 +158,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
model,
|
model,
|
||||||
*,
|
*,
|
||||||
fields,
|
fields,
|
||||||
|
expressions=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
MySQL can remove an implicit FK index on a field when that field is
|
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
|
Manually create an implicit FK index to make it possible to remove the
|
||||||
composed index.
|
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":
|
if first_field.get_internal_type() == "ForeignKey":
|
||||||
constraint_names = self._constraint_names(
|
column = self.connection.introspection.identifier_converter(
|
||||||
model,
|
first_field.column
|
||||||
[first_field.column],
|
|
||||||
index=True,
|
|
||||||
)
|
)
|
||||||
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.execute(
|
||||||
self._create_index_sql(model, fields=[first_field], suffix="")
|
self._create_index_sql(model, fields=[first_field], suffix="")
|
||||||
)
|
)
|
||||||
|
|
|
@ -2735,6 +2735,24 @@ class SchemaTests(TransactionTestCase):
|
||||||
editor.remove_index(Book, index)
|
editor.remove_index(Book, index)
|
||||||
self.assertNotIn(index.name, self.get_constraints(table))
|
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")
|
@skipUnlessDBFeature("supports_expression_indexes")
|
||||||
def test_composed_func_transform_index_with_fk(self):
|
def test_composed_func_transform_index_with_fk(self):
|
||||||
index = Index(F("title__lower"), name="book_title_lower_idx")
|
index = Index(F("title__lower"), name="book_title_lower_idx")
|
||||||
|
@ -2756,6 +2774,13 @@ class SchemaTests(TransactionTestCase):
|
||||||
editor.remove_constraint(Book, constraint)
|
editor.remove_constraint(Book, constraint)
|
||||||
self.assertNotIn(constraint.name, self.get_constraints(table))
|
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(
|
@skipUnlessDBFeature(
|
||||||
"supports_column_check_constraints", "can_introspect_check_constraints"
|
"supports_column_check_constraints", "can_introspect_check_constraints"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue