Commit 27b6f284 authored by Andrew Godwin's avatar Andrew Godwin
Browse files

Fixed #23264: Schema backends honour db_constraint

parent 8fe40686
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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:
@@ -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(
@@ -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" % (
@@ -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(
+9 −0
Original line number Diff line number Diff line
@@ -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)
+91 −2
Original line number Diff line number Diff line
@@ -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):
@@ -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
@@ -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