Refs #30172 -- Prevented removing a model Meta's index/unique_together from removing Meta constraints/indexes.
This commit is contained in:
parent
4bb859e246
commit
5c17c273ae
|
@ -384,8 +384,13 @@ class BaseDatabaseSchemaEditor:
|
|||
self.execute(self._create_index_sql(model, fields, suffix="_idx"))
|
||||
|
||||
def _delete_composed_index(self, model, fields, constraint_kwargs, sql):
|
||||
meta_constraint_names = {constraint.name for constraint in model._meta.constraints}
|
||||
meta_index_names = {constraint.name for constraint in model._meta.indexes}
|
||||
columns = [model._meta.get_field(field).column for field in fields]
|
||||
constraint_names = self._constraint_names(model, columns, **constraint_kwargs)
|
||||
constraint_names = self._constraint_names(
|
||||
model, columns, exclude=meta_constraint_names | meta_index_names,
|
||||
**constraint_kwargs
|
||||
)
|
||||
if len(constraint_names) != 1:
|
||||
raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % (
|
||||
len(constraint_names),
|
||||
|
@ -607,13 +612,15 @@ class BaseDatabaseSchemaEditor:
|
|||
meta_index_names = {index.name for index in model._meta.indexes}
|
||||
# Retrieve only BTREE indexes since this is what's created with
|
||||
# db_index=True.
|
||||
index_names = self._constraint_names(model, [old_field.column], index=True, type_=Index.suffix)
|
||||
index_names = self._constraint_names(
|
||||
model, [old_field.column], index=True, type_=Index.suffix,
|
||||
exclude=meta_index_names,
|
||||
)
|
||||
for index_name in index_names:
|
||||
if index_name not in meta_index_names:
|
||||
# The only way to check if an index was created with
|
||||
# db_index=True or with Index(['field'], name='foo')
|
||||
# is to look at its name (refs #28053).
|
||||
self.execute(self._delete_index_sql(model, index_name))
|
||||
# The only way to check if an index was created with
|
||||
# db_index=True or with Index(['field'], name='foo')
|
||||
# is to look at its name (refs #28053).
|
||||
self.execute(self._delete_index_sql(model, index_name))
|
||||
# Change check constraints?
|
||||
if old_db_params['check'] != new_db_params['check'] and old_db_params['check']:
|
||||
meta_constraint_names = {constraint.name for constraint in model._meta.constraints}
|
||||
|
|
|
@ -62,6 +62,24 @@ class AuthorWithUniqueName(models.Model):
|
|||
apps = new_apps
|
||||
|
||||
|
||||
class AuthorWithIndexedNameAndBirthday(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
birthday = models.DateField()
|
||||
|
||||
class Meta:
|
||||
apps = new_apps
|
||||
index_together = [['name', 'birthday']]
|
||||
|
||||
|
||||
class AuthorWithUniqueNameAndBirthday(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
birthday = models.DateField()
|
||||
|
||||
class Meta:
|
||||
apps = new_apps
|
||||
unique_together = [['name', 'birthday']]
|
||||
|
||||
|
||||
class Book(models.Model):
|
||||
author = models.ForeignKey(Author, models.CASCADE)
|
||||
title = models.CharField(max_length=100, db_index=True)
|
||||
|
|
|
@ -32,10 +32,11 @@ from .fields import (
|
|||
from .models import (
|
||||
Author, AuthorCharFieldWithIndex, AuthorTextFieldWithIndex,
|
||||
AuthorWithDefaultHeight, AuthorWithEvenLongerName, AuthorWithIndexedName,
|
||||
AuthorWithUniqueName, Book, BookForeignObj, BookWeak, BookWithLongName,
|
||||
BookWithO2O, BookWithoutAuthor, BookWithSlug, IntegerPK, Node, Note,
|
||||
NoteRename, Tag, TagIndexed, TagM2MTest, TagUniqueRename, Thing,
|
||||
UniqueTest, new_apps,
|
||||
AuthorWithIndexedNameAndBirthday, AuthorWithUniqueName,
|
||||
AuthorWithUniqueNameAndBirthday, Book, BookForeignObj, BookWeak,
|
||||
BookWithLongName, BookWithO2O, BookWithoutAuthor, BookWithSlug, IntegerPK,
|
||||
Node, Note, NoteRename, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
|
||||
Thing, UniqueTest, new_apps,
|
||||
)
|
||||
|
||||
|
||||
|
@ -1839,6 +1840,50 @@ class SchemaTests(TransactionTestCase):
|
|||
with connection.schema_editor() as editor:
|
||||
editor.alter_unique_together(Book, [['author', 'title']], [])
|
||||
|
||||
@skipUnlessDBFeature('allows_multiple_constraints_on_same_fields')
|
||||
def test_remove_unique_together_does_not_remove_meta_constraints(self):
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(AuthorWithUniqueNameAndBirthday)
|
||||
# Add the custom unique constraint
|
||||
constraint = UniqueConstraint(fields=['name', 'birthday'], name='author_name_birthday_uniq')
|
||||
custom_constraint_name = constraint.name
|
||||
AuthorWithUniqueNameAndBirthday._meta.constraints = [constraint]
|
||||
with connection.schema_editor() as editor:
|
||||
editor.add_constraint(AuthorWithUniqueNameAndBirthday, constraint)
|
||||
# Ensure the constraints exist
|
||||
constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_constraint_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 1)
|
||||
# Remove unique together
|
||||
unique_together = AuthorWithUniqueNameAndBirthday._meta.unique_together
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, unique_together, [])
|
||||
constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_constraint_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 0)
|
||||
# Re-add unique together
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, [], unique_together)
|
||||
constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_constraint_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 1)
|
||||
# Drop the unique constraint
|
||||
with connection.schema_editor() as editor:
|
||||
AuthorWithUniqueNameAndBirthday._meta.constraints = []
|
||||
editor.remove_constraint(AuthorWithUniqueNameAndBirthday, constraint)
|
||||
|
||||
def test_index_together(self):
|
||||
"""
|
||||
Tests removing and adding index_together constraints on a model.
|
||||
|
@ -1917,6 +1962,50 @@ class SchemaTests(TransactionTestCase):
|
|||
),
|
||||
)
|
||||
|
||||
@skipUnlessDBFeature('allows_multiple_constraints_on_same_fields')
|
||||
def test_remove_index_together_does_not_remove_meta_indexes(self):
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(AuthorWithIndexedNameAndBirthday)
|
||||
# Add the custom index
|
||||
index = Index(fields=['name', 'birthday'], name='author_name_birthday_idx')
|
||||
custom_index_name = index.name
|
||||
AuthorWithIndexedNameAndBirthday._meta.indexes = [index]
|
||||
with connection.schema_editor() as editor:
|
||||
editor.add_index(AuthorWithIndexedNameAndBirthday, index)
|
||||
# Ensure the indexes exist
|
||||
constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_index_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 1)
|
||||
# Remove index together
|
||||
index_together = AuthorWithIndexedNameAndBirthday._meta.index_together
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_index_together(AuthorWithIndexedNameAndBirthday, index_together, [])
|
||||
constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_index_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 0)
|
||||
# Re-add index together
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_index_together(AuthorWithIndexedNameAndBirthday, [], index_together)
|
||||
constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
|
||||
self.assertIn(custom_index_name, constraints)
|
||||
other_constraints = [
|
||||
name for name, details in constraints.items()
|
||||
if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
|
||||
]
|
||||
self.assertEqual(len(other_constraints), 1)
|
||||
# Drop the index
|
||||
with connection.schema_editor() as editor:
|
||||
AuthorWithIndexedNameAndBirthday._meta.indexes = []
|
||||
editor.remove_index(AuthorWithIndexedNameAndBirthday, index)
|
||||
|
||||
@isolate_apps('schema')
|
||||
def test_db_table(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue