diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index 91ee3dac22..c24e809570 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -727,7 +727,7 @@ class BaseDatabaseSchemaEditor: # 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: for rel in new_field.model._meta.related_objects: - if not rel.many_to_many: + if not rel.many_to_many and rel.field.db_constraint: self.execute(self._create_fk_sql(rel.related_model, rel.field, "_fk")) # Does it have check constraints we need to add? if old_db_params['check'] != new_db_params['check'] and new_db_params['check']: diff --git a/docs/releases/1.11.3.txt b/docs/releases/1.11.3.txt index 9aab4fbfb7..1c716bf5a3 100644 --- a/docs/releases/1.11.3.txt +++ b/docs/releases/1.11.3.txt @@ -51,3 +51,6 @@ Bugfixes 1.11.1, casting was added in Python to avoid localization of numeric values in Django templates, but this made some use cases more difficult. Casting is now done in the template using the ``|stringformat:'s'`` filter. + +* Prevented a primary key alteration from adding a foreign key constraint if + ``db_constraint=False`` (:ticket:`28298`). diff --git a/tests/schema/tests.py b/tests/schema/tests.py index b889f6cda1..e2b4f8ca6b 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -22,7 +22,7 @@ from django.db.transaction import TransactionManagementError, atomic from django.test import ( TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, ) -from django.test.utils import CaptureQueriesContext +from django.test.utils import CaptureQueriesContext, isolate_apps from django.utils import timezone from .fields import ( @@ -319,6 +319,37 @@ class SchemaTests(TransactionTestCase): editor.alter_field(Author, new_field2, new_field, strict=True) self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag') + @isolate_apps('schema') + def test_no_db_constraint_added_during_primary_key_change(self): + """ + When a primary key that's pointed to by a ForeignKey with + db_constraint=False is altered, a foreign key constraint isn't added. + """ + class Author(Model): + class Meta: + app_label = 'schema' + + class BookWeak(Model): + author = ForeignKey(Author, CASCADE, db_constraint=False) + + class Meta: + app_label = 'schema' + + with connection.schema_editor() as editor: + editor.create_model(Author) + editor.create_model(BookWeak) + self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author') + old_field = Author._meta.get_field('id') + new_field = BigAutoField(primary_key=True) + new_field.model = Author + new_field.set_attributes_from_name('id') + # @isolate_apps() and inner models are needed to have the model + # relations populated, otherwise this doesn't act as a regression test. + self.assertEqual(len(new_field.model._meta.related_objects), 1) + with connection.schema_editor() as editor: + editor.alter_field(Author, old_field, new_field, strict=True) + self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author') + def _test_m2m_db_constraint(self, M2MFieldClass): class LocalAuthorWithM2M(Model): name = CharField(max_length=255)