Loading django/db/backends/schema.py +6 −4 Original line number Diff line number Diff line Loading @@ -228,7 +228,7 @@ class BaseDatabaseSchemaEditor(object): } ) # FK if field.rel: if field.rel and field.db_constraint: to_table = field.rel.to._meta.db_table to_column = field.rel.to._meta.get_field(field.rel.field_name).column if self.connection.features.supports_foreign_keys: Loading Loading @@ -430,7 +430,7 @@ class BaseDatabaseSchemaEditor(object): } ) # 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_column = field.rel.to._meta.get_field(field.rel.field_name).column self.deferred_sql.append( Loading Loading @@ -530,7 +530,7 @@ class BaseDatabaseSchemaEditor(object): ) # Drop any FK constraints, we'll remake them later 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) if strict and len(fk_names) != 1: raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % ( Loading Loading @@ -739,7 +739,9 @@ class BaseDatabaseSchemaEditor(object): } ) # 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_column = new_field.rel.get_related_field().column self.execute( Loading tests/schema/models.py +9 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,15 @@ class Book(models.Model): 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): author = models.ForeignKey(Author) title = models.CharField(max_length=100, db_index=True) Loading tests/schema/tests.py +91 −2 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ from django.db.transaction import atomic from .models import (Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough, AuthorWithEvenLongerName) AuthorWithEvenLongerName, BookWeak) class SchemaTests(TransactionTestCase): Loading @@ -27,7 +27,8 @@ class SchemaTests(TransactionTestCase): models = [ Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName, BookWeak, ] # Utility functions Loading Loading @@ -150,6 +151,94 @@ class SchemaTests(TransactionTestCase): else: 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): """ Tests adding fields to models Loading Loading
django/db/backends/schema.py +6 −4 Original line number Diff line number Diff line Loading @@ -228,7 +228,7 @@ class BaseDatabaseSchemaEditor(object): } ) # FK if field.rel: if field.rel and field.db_constraint: to_table = field.rel.to._meta.db_table to_column = field.rel.to._meta.get_field(field.rel.field_name).column if self.connection.features.supports_foreign_keys: Loading Loading @@ -430,7 +430,7 @@ class BaseDatabaseSchemaEditor(object): } ) # 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_column = field.rel.to._meta.get_field(field.rel.field_name).column self.deferred_sql.append( Loading Loading @@ -530,7 +530,7 @@ class BaseDatabaseSchemaEditor(object): ) # Drop any FK constraints, we'll remake them later 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) if strict and len(fk_names) != 1: raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % ( Loading Loading @@ -739,7 +739,9 @@ class BaseDatabaseSchemaEditor(object): } ) # 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_column = new_field.rel.get_related_field().column self.execute( Loading
tests/schema/models.py +9 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,15 @@ class Book(models.Model): 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): author = models.ForeignKey(Author) title = models.CharField(max_length=100, db_index=True) Loading
tests/schema/tests.py +91 −2 Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ from django.db.transaction import atomic from .models import (Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough, AuthorWithEvenLongerName) AuthorWithEvenLongerName, BookWeak) class SchemaTests(TransactionTestCase): Loading @@ -27,7 +27,8 @@ class SchemaTests(TransactionTestCase): models = [ Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName, BookWeak, ] # Utility functions Loading Loading @@ -150,6 +151,94 @@ class SchemaTests(TransactionTestCase): else: 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): """ Tests adding fields to models Loading