Fixed #28298 -- Prevented a primary key alteration from adding a foreign key constraint if db_constraint=False.

This commit is contained in:
Josh Schneier 2017-06-11 20:05:54 -04:00 committed by Tim Graham
parent 0af14b2eaa
commit fba0eaa5d6
3 changed files with 36 additions and 2 deletions

View File

@ -727,7 +727,7 @@ class BaseDatabaseSchemaEditor:
# Rebuild FKs that pointed to us if we previously had to drop them # 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: if old_field.primary_key and new_field.primary_key and old_type != new_type:
for rel in new_field.model._meta.related_objects: 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")) self.execute(self._create_fk_sql(rel.related_model, rel.field, "_fk"))
# Does it have check constraints we need to add? # Does it have check constraints we need to add?
if old_db_params['check'] != new_db_params['check'] and new_db_params['check']: if old_db_params['check'] != new_db_params['check'] and new_db_params['check']:

View File

@ -51,3 +51,6 @@ Bugfixes
1.11.1, casting was added in Python to avoid localization of numeric values 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 in Django templates, but this made some use cases more difficult. Casting is
now done in the template using the ``|stringformat:'s'`` filter. 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`).

View File

@ -22,7 +22,7 @@ from django.db.transaction import TransactionManagementError, atomic
from django.test import ( from django.test import (
TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature,
) )
from django.test.utils import CaptureQueriesContext from django.test.utils import CaptureQueriesContext, isolate_apps
from django.utils import timezone from django.utils import timezone
from .fields import ( from .fields import (
@ -319,6 +319,37 @@ class SchemaTests(TransactionTestCase):
editor.alter_field(Author, new_field2, new_field, strict=True) editor.alter_field(Author, new_field2, new_field, strict=True)
self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag') 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): def _test_m2m_db_constraint(self, M2MFieldClass):
class LocalAuthorWithM2M(Model): class LocalAuthorWithM2M(Model):
name = CharField(max_length=255) name = CharField(max_length=255)