Fixed #23264: Schema backends honour db_constraint
This commit is contained in:
parent
8fe406864c
commit
27b6f28435
|
@ -228,7 +228,7 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# FK
|
# FK
|
||||||
if field.rel:
|
if field.rel and field.db_constraint:
|
||||||
to_table = field.rel.to._meta.db_table
|
to_table = field.rel.to._meta.db_table
|
||||||
to_column = field.rel.to._meta.get_field(field.rel.field_name).column
|
to_column = field.rel.to._meta.get_field(field.rel.field_name).column
|
||||||
if self.connection.features.supports_foreign_keys:
|
if self.connection.features.supports_foreign_keys:
|
||||||
|
@ -430,7 +430,7 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# Add any FK constraints later
|
# Add any FK constraints later
|
||||||
if field.rel and self.connection.features.supports_foreign_keys:
|
if field.rel and self.connection.features.supports_foreign_keys and field.db_constraint:
|
||||||
to_table = field.rel.to._meta.db_table
|
to_table = field.rel.to._meta.db_table
|
||||||
to_column = field.rel.to._meta.get_field(field.rel.field_name).column
|
to_column = field.rel.to._meta.get_field(field.rel.field_name).column
|
||||||
self.deferred_sql.append(
|
self.deferred_sql.append(
|
||||||
|
@ -530,7 +530,7 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
)
|
)
|
||||||
# Drop any FK constraints, we'll remake them later
|
# Drop any FK constraints, we'll remake them later
|
||||||
fks_dropped = set()
|
fks_dropped = set()
|
||||||
if old_field.rel:
|
if old_field.rel and old_field.db_constraint:
|
||||||
fk_names = self._constraint_names(model, [old_field.column], foreign_key=True)
|
fk_names = self._constraint_names(model, [old_field.column], foreign_key=True)
|
||||||
if strict and len(fk_names) != 1:
|
if strict and len(fk_names) != 1:
|
||||||
raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % (
|
raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % (
|
||||||
|
@ -739,7 +739,9 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# Does it have a foreign key?
|
# Does it have a foreign key?
|
||||||
if new_field.rel and fks_dropped:
|
if new_field.rel and \
|
||||||
|
(fks_dropped or (old_field.rel and not old_field.db_constraint)) and \
|
||||||
|
new_field.db_constraint:
|
||||||
to_table = new_field.rel.to._meta.db_table
|
to_table = new_field.rel.to._meta.db_table
|
||||||
to_column = new_field.rel.get_related_field().column
|
to_column = new_field.rel.get_related_field().column
|
||||||
self.execute(
|
self.execute(
|
||||||
|
|
|
@ -50,6 +50,15 @@ class Book(models.Model):
|
||||||
apps = new_apps
|
apps = new_apps
|
||||||
|
|
||||||
|
|
||||||
|
class BookWeak(models.Model):
|
||||||
|
author = models.ForeignKey(Author, db_constraint=False)
|
||||||
|
title = models.CharField(max_length=100, db_index=True)
|
||||||
|
pub_date = models.DateTimeField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
apps = new_apps
|
||||||
|
|
||||||
|
|
||||||
class BookWithM2M(models.Model):
|
class BookWithM2M(models.Model):
|
||||||
author = models.ForeignKey(Author)
|
author = models.ForeignKey(Author)
|
||||||
title = models.CharField(max_length=100, db_index=True)
|
title = models.CharField(max_length=100, db_index=True)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.db.transaction import atomic
|
||||||
from .models import (Author, AuthorWithM2M, Book, BookWithLongName,
|
from .models import (Author, AuthorWithM2M, Book, BookWithLongName,
|
||||||
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
|
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
|
||||||
UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
|
UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
|
||||||
AuthorWithEvenLongerName)
|
AuthorWithEvenLongerName, BookWeak)
|
||||||
|
|
||||||
|
|
||||||
class SchemaTests(TransactionTestCase):
|
class SchemaTests(TransactionTestCase):
|
||||||
|
@ -27,7 +27,8 @@ class SchemaTests(TransactionTestCase):
|
||||||
models = [
|
models = [
|
||||||
Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
|
Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
|
||||||
BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
|
BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
|
||||||
Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName
|
Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName,
|
||||||
|
BookWeak,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Utility functions
|
# Utility functions
|
||||||
|
@ -150,6 +151,94 @@ class SchemaTests(TransactionTestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("No FK constraint for author_id found")
|
self.fail("No FK constraint for author_id found")
|
||||||
|
|
||||||
|
@unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
|
||||||
|
def test_fk_db_constraint(self):
|
||||||
|
"Tests that the db_constraint parameter is respected"
|
||||||
|
# Create the table
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.create_model(Tag)
|
||||||
|
editor.create_model(Author)
|
||||||
|
editor.create_model(BookWeak)
|
||||||
|
# Check that initial tables are there
|
||||||
|
list(Author.objects.all())
|
||||||
|
list(Tag.objects.all())
|
||||||
|
list(BookWeak.objects.all())
|
||||||
|
# Check that BookWeak doesn't have an FK constraint
|
||||||
|
constraints = self.get_constraints(BookWeak._meta.db_table)
|
||||||
|
for name, details in constraints.items():
|
||||||
|
if details['columns'] == ["author_id"] and details['foreign_key']:
|
||||||
|
self.fail("FK constraint for author_id found")
|
||||||
|
# Make a db_constraint=False FK
|
||||||
|
new_field = ForeignKey(Tag, db_constraint=False)
|
||||||
|
new_field.set_attributes_from_name("tag")
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.add_field(
|
||||||
|
Author,
|
||||||
|
new_field,
|
||||||
|
)
|
||||||
|
# Make sure no FK constraint is present
|
||||||
|
constraints = self.get_constraints(Author._meta.db_table)
|
||||||
|
for name, details in constraints.items():
|
||||||
|
if details['columns'] == ["tag_id"] and details['foreign_key']:
|
||||||
|
self.fail("FK constraint for tag_id found")
|
||||||
|
# Alter to one with a constraint
|
||||||
|
new_field_2 = ForeignKey(Tag)
|
||||||
|
new_field_2.set_attributes_from_name("tag")
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.alter_field(
|
||||||
|
Author,
|
||||||
|
new_field,
|
||||||
|
new_field_2,
|
||||||
|
strict=True,
|
||||||
|
)
|
||||||
|
# Make sure the new FK constraint is present
|
||||||
|
constraints = self.get_constraints(Author._meta.db_table)
|
||||||
|
for name, details in constraints.items():
|
||||||
|
if details['columns'] == ["tag_id"] and details['foreign_key']:
|
||||||
|
self.assertEqual(details['foreign_key'], ('schema_tag', 'id'))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.fail("No FK constraint for tag_id found")
|
||||||
|
# Alter to one without a constraint again
|
||||||
|
new_field_2 = ForeignKey(Tag)
|
||||||
|
new_field_2.set_attributes_from_name("tag")
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.alter_field(
|
||||||
|
Author,
|
||||||
|
new_field_2,
|
||||||
|
new_field,
|
||||||
|
strict=True,
|
||||||
|
)
|
||||||
|
# Make sure no FK constraint is present
|
||||||
|
constraints = self.get_constraints(Author._meta.db_table)
|
||||||
|
for name, details in constraints.items():
|
||||||
|
if details['columns'] == ["tag_id"] and details['foreign_key']:
|
||||||
|
self.fail("FK constraint for tag_id found")
|
||||||
|
|
||||||
|
@unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
|
||||||
|
def test_m2m_db_constraint(self):
|
||||||
|
# Create the table
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.create_model(Tag)
|
||||||
|
editor.create_model(Author)
|
||||||
|
# Check that initial tables are there
|
||||||
|
list(Author.objects.all())
|
||||||
|
list(Tag.objects.all())
|
||||||
|
# Make a db_constraint=False FK
|
||||||
|
new_field = ManyToManyField("schema.Tag", related_name="authors", db_constraint=False)
|
||||||
|
new_field.contribute_to_class(Author, "tags")
|
||||||
|
# Add the field
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
editor.add_field(
|
||||||
|
Author,
|
||||||
|
new_field,
|
||||||
|
)
|
||||||
|
# Make sure no FK constraint is present
|
||||||
|
constraints = self.get_constraints(new_field.rel.through._meta.db_table)
|
||||||
|
for name, details in constraints.items():
|
||||||
|
if details['columns'] == ["tag_id"] and details['foreign_key']:
|
||||||
|
self.fail("FK constraint for tag_id found")
|
||||||
|
|
||||||
def test_add_field(self):
|
def test_add_field(self):
|
||||||
"""
|
"""
|
||||||
Tests adding fields to models
|
Tests adding fields to models
|
||||||
|
|
Loading…
Reference in New Issue