#36428: Collector.delete() calls sort() but does not order deletions correctly when
nullable FK is present with non-null value
-------------------------------------+-------------------------------------
Reporter: Andréas Kühne | Owner: (none)
Type: Bug | Status: closed
Component: Database layer | Version: 5.1
(models, ORM) |
Severity: Normal | Resolution: invalid
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Simon Charette):
* resolution: => invalid
* status: new => closed
Comment:
Hello Andréas, I unfortunately cannot reproduce against `main`, `5.2.x`,
or `5.1.6` with the following test against SQLite and Postgres
{{{#!diff
diff --git a/tests/delete/models.py b/tests/delete/models.py
index 4b627712bb..024466fef6 100644
--- a/tests/delete/models.py
+++ b/tests/delete/models.py
@@ -241,3 +241,18 @@ class GenericDeleteBottomParent(models.Model):
generic_delete_bottom = models.ForeignKey(
GenericDeleteBottom, on_delete=models.CASCADE
)
+
+
+class ActivityLocation(models.Model):
+ name = models.CharField(max_length=100)
+
+
+class BookableItem(models.Model):
+ name = models.CharField(max_length=100)
+ location = models.ForeignKey(
+ ActivityLocation,
+ on_delete=models.CASCADE,
+ related_name="bookable_items",
+ null=True,
+ blank=True,
+ )
diff --git a/tests/delete/tests.py b/tests/delete/tests.py
index 01228631f4..c0c03411c7 100644
--- a/tests/delete/tests.py
+++ b/tests/delete/tests.py
@@ -4,7 +4,12 @@
from django.db.models import ProtectedError, Q, RestrictedError
from django.db.models.deletion import Collector
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE
-from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
+from django.test import (
+ TestCase,
+ TransactionTestCase,
+ skipIfDBFeature,
+ skipUnlessDBFeature,
+)
from .models import (
B1,
@@ -800,3 +805,17 @@ def test_fast_delete_full_match(self):
with self.assertNumQueries(1):
User.objects.filter(~Q(pk__in=[]) |
Q(avatar__desc="foo")).delete()
self.assertFalse(User.objects.exists())
+
+
+class Ticket36428Tests(TransactionTestCase):
+ available_apps = ["delete"]
+
+ def test_order(self):
+ from .models import ActivityLocation, BookableItem
+
+ location = ActivityLocation.objects.create(name="Test Location")
+ item = BookableItem.objects.create(name="Test Item",
location=location)
+
+ # Try to delete the location
+ location.delete()
}}}
In all cases the issued SQL is correct and of the form
{{{#!sql
BEGIN;
---
DELETE
FROM "delete_bookableitem"
WHERE "delete_bookableitem"."location_id" IN (1);
---
DELETE
FROM "delete_activitylocation"
WHERE "delete_activitylocation"."id" IN (1);
---
COMMIT;
}}}
It would be quite surprising if cascade deletion dependency resolving was
broken since 5.1.x given the way it's extensively tested and used in the
wild so I highly suspect something else is at play here.
Perhaps you have model signal receivers connected (`post_save`,
`post_delete`) that might be interfering?
--
Ticket URL: <
https://code.djangoproject.com/ticket/36428#comment:1>