Commit 283b630d authored by Jean-Louis Fuchs's avatar Jean-Louis Fuchs Committed by Markus Holtermann
Browse files

Fixed #24447 -- Made migrations add FK constraints for existing columns

When altering from e.g. an IntegerField to a ForeignKey, Django didn't
add a constraint.

Backport of f4f0060f from master
parent 7f2d6099
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -675,9 +675,9 @@ class BaseDatabaseSchemaEditor(object):
                }
            )
        # Does it have a foreign key?
        if new_field.rel and \
           (fks_dropped or (old_field.rel and not old_field.db_constraint)) and \
           new_field.db_constraint:
        if (new_field.rel and
                (fks_dropped or not old_field.rel or not old_field.db_constraint) and
                new_field.db_constraint):
            self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s"))
        # Rebuild FKs that pointed to us if we previously had to drop them
        if old_field.primary_key and new_field.primary_key and old_type != new_type:
+3 −0
Original line number Diff line number Diff line
@@ -11,3 +11,6 @@ Bugfixes

* Fixed crash when coercing ``ManyRelatedManager`` to a string
  (:ticket:`24352`).

* Fixed a bug that prevented migrations from adding a FK constraint
  for an existing column. (:ticket:`24447`).
+9 −0
Original line number Diff line number Diff line
@@ -87,6 +87,15 @@ class BookWithM2M(models.Model):
        apps = new_apps


class BookWithoutFK(models.Model):
    author = models.IntegerField()
    title = models.CharField(max_length=100, db_index=True)
    pub_date = models.DateTimeField()

    class Meta:
        apps = new_apps


class TagThrough(models.Model):
    book = models.ForeignKey("schema.BookWithM2MThrough")
    tag = models.ForeignKey("schema.TagM2MTest")
+34 −2
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ from .fields import CustomManyToManyField, InheritedManyToManyField
from .models import (Author, AuthorWithDefaultHeight, AuthorWithM2M, Book, BookWithLongName,
    BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
    UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
    AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O)
    AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O, BookWithoutFK)


class SchemaTests(TransactionTestCase):
@@ -29,7 +29,7 @@ class SchemaTests(TransactionTestCase):
        Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
        BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
        Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName,
        BookWeak, BookWithO2O,
        BookWeak, BookWithO2O, BookWithoutFK,
    ]

    # Utility functions
@@ -536,6 +536,38 @@ 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_alter_to_fk(self):
        """
        #24447 - Tests adding a FK constraint for an existing column
        """
        # Create the tables
        with connection.schema_editor() as editor:
            editor.create_model(Author)
            editor.create_model(BookWithoutFK)
        # Ensure no FK constraint exists
        constraints = self.get_constraints(BookWithoutFK._meta.db_table)
        for name, details in constraints.items():
            if details['foreign_key']:
                self.fail('Found an unexpected FK constraint to %s' % details['columns'])
        new_field = ForeignKey(Author)
        new_field.set_attributes_from_name("author")
        with connection.schema_editor() as editor:
            editor.alter_field(
                BookWithoutFK,
                BookWithoutFK._meta.get_field_by_name("author")[0],
                new_field,
                strict=True,
            )
        constraints = self.get_constraints(BookWithoutFK._meta.db_table)
        # Ensure FK constraint exists
        for name, details in constraints.items():
            if details['foreign_key'] and details['columns'] == ["author_id"]:
                self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
                break
        else:
            self.fail("No FK constraint for author_id found")

    @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
    def test_alter_o2o_to_fk(self):
        """