Skip to content

Commit

Permalink
[1.11.x] Fixed #29016 -- Fixed incorrect foreign key nullification on…
Browse files Browse the repository at this point in the history
… related instance deletion.

Backport of 9a621ed from master
  • Loading branch information
Nimn authored and timgraham committed Jan 13, 2018
1 parent fb56038 commit 419705b
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 4 deletions.
2 changes: 1 addition & 1 deletion django/db/models/deletion.py
Expand Up @@ -286,8 +286,8 @@ def delete(self):


# update fields # update fields
for model, instances_for_fieldvalues in six.iteritems(self.field_updates): for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
query = sql.UpdateQuery(model)
for (field, value), instances in six.iteritems(instances_for_fieldvalues): for (field, value), instances in six.iteritems(instances_for_fieldvalues):
query = sql.UpdateQuery(model)
query.update_batch([obj.pk for obj in instances], query.update_batch([obj.pk for obj in instances],
{field.name: value}, self.using) {field.name: value}, self.using)


Expand Down
3 changes: 2 additions & 1 deletion docs/releases/1.11.10.txt
Expand Up @@ -9,4 +9,5 @@ Django 1.11.10 fixes several bugs in 1.11.9.
Bugfixes Bugfixes
======== ========


* ... * Fixed incorrect foreign key nullification if a model has two foreign keys to
the same model and a target model is deleted (:ticket:`29016`).
2 changes: 2 additions & 0 deletions tests/delete_regress/models.py
Expand Up @@ -56,6 +56,8 @@ class Email(Contact):


class Researcher(models.Model): class Researcher(models.Model):
contacts = models.ManyToManyField(Contact, related_name="research_contacts") contacts = models.ManyToManyField(Contact, related_name="research_contacts")
primary_contact = models.ForeignKey(Contact, models.SET_NULL, null=True, related_name='primary_contacts')
secondary_contact = models.ForeignKey(Contact, models.SET_NULL, null=True, related_name='secondary_contacts')




class Food(models.Model): class Food(models.Model):
Expand Down
28 changes: 26 additions & 2 deletions tests/delete_regress/tests.py
Expand Up @@ -6,7 +6,7 @@
from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature


from .models import ( from .models import (
Award, AwardNote, Book, Child, Eaten, Email, File, Food, FooFile, Award, AwardNote, Book, Child, Contact, Eaten, Email, File, Food, FooFile,
FooFileProxy, FooImage, FooPhoto, House, Image, Item, Location, Login, FooFileProxy, FooImage, FooPhoto, House, Image, Item, Location, Login,
OrderedPerson, OrgUnit, Person, Photo, PlayedWith, PlayedWithNote, Policy, OrderedPerson, OrgUnit, Person, Photo, PlayedWith, PlayedWithNote, Policy,
Researcher, Toy, Version, Researcher, Toy, Version,
Expand Down Expand Up @@ -335,7 +335,7 @@ def test_ticket_19102_defer(self):
self.assertTrue(Login.objects.filter(pk=self.l2.pk).exists()) self.assertTrue(Login.objects.filter(pk=self.l2.pk).exists())




class OrderedDeleteTests(TestCase): class DeleteTests(TestCase):
def test_meta_ordered_delete(self): def test_meta_ordered_delete(self):
# When a subquery is performed by deletion code, the subquery must be # When a subquery is performed by deletion code, the subquery must be
# cleared of all ordering. There was a but that caused _meta ordering # cleared of all ordering. There was a but that caused _meta ordering
Expand All @@ -345,3 +345,27 @@ def test_meta_ordered_delete(self):
OrderedPerson.objects.create(name='Bob', lives_in=h) OrderedPerson.objects.create(name='Bob', lives_in=h)
OrderedPerson.objects.filter(lives_in__address='Foo').delete() OrderedPerson.objects.filter(lives_in__address='Foo').delete()
self.assertEqual(OrderedPerson.objects.count(), 0) self.assertEqual(OrderedPerson.objects.count(), 0)

def test_foreign_key_delete_nullifies_correct_columns(self):
"""
With a model (Researcher) that has two foreign keys pointing to the
same model (Contact), deleting an instance of the target model
(contact1) nullifies the correct fields of Researcher.
"""
contact1 = Contact.objects.create(label='Contact 1')
contact2 = Contact.objects.create(label='Contact 2')
researcher1 = Researcher.objects.create(
primary_contact=contact1,
secondary_contact=contact2,
)
researcher2 = Researcher.objects.create(
primary_contact=contact2,
secondary_contact=contact1,
)
contact1.delete()
researcher1.refresh_from_db()
researcher2.refresh_from_db()
self.assertIsNone(researcher1.primary_contact)
self.assertEqual(researcher1.secondary_contact, contact2)
self.assertEqual(researcher2.primary_contact, contact2)
self.assertIsNone(researcher2.secondary_contact)

0 comments on commit 419705b

Please sign in to comment.