From 6e87dacf62e742b78787659b9a51561fd5465682 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Mon, 30 May 2011 16:19:53 +0000 Subject: [PATCH] [1.3.X] Fixed #15776 - delete regression in Django 1.3 involving nullable foreign keys Many thanks to aaron.l.madison for the detailed report and to emulbreh for the fix. Backport of [16295] from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.3.X@16296 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/deletion.py | 6 +++--- tests/regressiontests/delete_regress/models.py | 16 ++++++++++++++++ tests/regressiontests/delete_regress/tests.py | 10 +++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index 73960f5e3c..8f023f4109 100644 --- a/django/db/models/deletion.py +++ b/django/db/models/deletion.py @@ -83,8 +83,8 @@ class Collector(object): def add(self, objs, source=None, nullable=False, reverse_dependency=False): """ Adds 'objs' to the collection of objects to be deleted. If the call is - the result of a cascade, 'source' should be the model that caused it - and 'nullable' should be set to True, if the relation can be null. + the result of a cascade, 'source' should be the model that caused it, + and 'nullable' should be set to True if the relation can be null. Returns a list of all objects that were not already collected. """ @@ -100,7 +100,7 @@ class Collector(object): # Nullable relationships can be ignored -- they are nulled out before # deleting, and therefore do not affect the order in which objects have # to be deleted. - if new_objs and source is not None and not nullable: + if source is not None and not nullable: if reverse_dependency: source, model = model, source self.dependencies.setdefault(source, set()).add(model) diff --git a/tests/regressiontests/delete_regress/models.py b/tests/regressiontests/delete_regress/models.py index 5c77117719..8e3d0d55b1 100644 --- a/tests/regressiontests/delete_regress/models.py +++ b/tests/regressiontests/delete_regress/models.py @@ -51,3 +51,19 @@ class Food(models.Model): class Eaten(models.Model): food = models.ForeignKey(Food, to_field="name") meal = models.CharField(max_length=20) + + +# Models for #15776 + +class Policy(models.Model): + policy_number = models.CharField(max_length=10) + +class Version(models.Model): + policy = models.ForeignKey(Policy) + +class Location(models.Model): + version = models.ForeignKey(Version, blank=True, null=True) + +class Item(models.Model): + version = models.ForeignKey(Version) + location = models.ForeignKey(Location, blank=True, null=True) diff --git a/tests/regressiontests/delete_regress/tests.py b/tests/regressiontests/delete_regress/tests.py index 20b9382c31..2027780329 100644 --- a/tests/regressiontests/delete_regress/tests.py +++ b/tests/regressiontests/delete_regress/tests.py @@ -5,7 +5,8 @@ from django.db import backend, connection, transaction, DEFAULT_DB_ALIAS from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature from models import (Book, Award, AwardNote, Person, Child, Toy, PlayedWith, - PlayedWithNote, Contact, Email, Researcher, Food, Eaten) + PlayedWithNote, Contact, Email, Researcher, Food, Eaten, + Policy, Version, Location, Item) # Can't run this test under SQLite, because you can't @@ -102,6 +103,13 @@ class DeleteCascadeTests(TestCase): # first two asserts just sanity checks, this is the kicker: self.assertEqual(PlayedWithNote.objects.count(), 0) + def test_15776(self): + policy = Policy.objects.create(pk=1, policy_number="1234") + version = Version.objects.create(policy=policy) + location = Location.objects.create(version=version) + item = Item.objects.create(version=version, location=location) + policy.delete() + class DeleteCascadeTransactionTests(TransactionTestCase): def test_inheritance(self):