Code

Ticket #6870: 6870.pre_delete. r13248.diff

File 6870.pre_delete. r13248.diff, 5.2 KB (added by cgieringer, 4 years ago)

Adds a pre_signal argument to _collect_sub_objects to allow the notification of listeners to pre_deletes before related objects are collected.

Line 
1diff --git a/django/db/models/base.py b/django/db/models/base.py
2index 6304e00..3a4d5c2 100644
3--- a/django/db/models/base.py
4+++ b/django/db/models/base.py
5@@ -544,7 +544,7 @@ class Model(object):
6 
7     save_base.alters_data = True
8 
9-    def _collect_sub_objects(self, seen_objs, parent=None, nullable=False):
10+    def _collect_sub_objects(self, seen_objs, parent=None, nullable=False, pre_signal=None):
11         """
12         Recursively populates seen_objs with all objects related to this
13         object.
14@@ -553,6 +553,8 @@ class Model(object):
15             [(model_class, {pk_val: obj, pk_val: obj, ...}),
16              (model_class, {pk_val: obj, pk_val: obj, ...}), ...]
17         """
18+        if not pre_signal is None and not self.__class__._meta.auto_created:
19+            pre_signal.send(sender=self.__class__, instance=self)
20         pk_val = self._get_pk_val()
21         if seen_objs.add(self.__class__, pk_val, self,
22                          type(parent), parent, nullable):
23@@ -566,7 +568,7 @@ class Model(object):
24                 except ObjectDoesNotExist:
25                     pass
26                 else:
27-                    sub_obj._collect_sub_objects(seen_objs, self, related.field.null)
28+                    sub_obj._collect_sub_objects(seen_objs, self, related.field.null, pre_signal)
29             else:
30                 # To make sure we can access all elements, we can't use the
31                 # normal manager on the related object. So we work directly
32@@ -584,7 +586,7 @@ class Model(object):
33                         continue
34                 delete_qs = rel_descriptor.delete_manager(self).all()
35                 for sub_obj in delete_qs:
36-                    sub_obj._collect_sub_objects(seen_objs, self, related.field.null)
37+                    sub_obj._collect_sub_objects(seen_objs, self, related.field.null, pre_signal)
38 
39         for related in self._meta.get_all_related_many_to_many_objects():
40             if related.field.rel.through:
41@@ -594,7 +596,7 @@ class Model(object):
42                 nullable = opts.get_field(reverse_field_name).null
43                 filters = {reverse_field_name: self}
44                 for sub_obj in related.field.rel.through._base_manager.using(db).filter(**filters):
45-                    sub_obj._collect_sub_objects(seen_objs, self, nullable)
46+                    sub_obj._collect_sub_objects(seen_objs, self, nullable, pre_signal)
47 
48         for f in self._meta.many_to_many:
49             if f.rel.through:
50@@ -604,13 +606,13 @@ class Model(object):
51                 nullable = opts.get_field(field_name).null
52                 filters = {field_name: self}
53                 for sub_obj in f.rel.through._base_manager.using(db).filter(**filters):
54-                    sub_obj._collect_sub_objects(seen_objs, self, nullable)
55+                    sub_obj._collect_sub_objects(seen_objs, self, nullable, pre_signal)
56             else:
57                 # m2m-ish but with no through table? GenericRelation: cascade delete
58                 for sub_obj in f.value_from_object(self).all():
59                     # Generic relations not enforced by db constraints, thus we can set
60                     # nullable=True, order does not matter
61-                    sub_obj._collect_sub_objects(seen_objs, self, True)
62+                    sub_obj._collect_sub_objects(seen_objs, self, True, pre_signal)
63 
64         # Handle any ancestors (for the model-inheritance case). We do this by
65         # traversing to the most remote parent classes -- those with no parents
66@@ -625,7 +627,7 @@ class Model(object):
67                 continue
68             # At this point, parent_obj is base class (no ancestor models). So
69             # delete it and all its descendents.
70-            parent_obj._collect_sub_objects(seen_objs)
71+            parent_obj._collect_sub_objects(seen_objs, pre_signal=pre_signal)
72 
73     def delete(self, using=None):
74         using = using or router.db_for_write(self.__class__, instance=self)
75@@ -633,7 +635,7 @@ class Model(object):
76 
77         # Find all the objects than need to be deleted.
78         seen_objs = CollectedObjects()
79-        self._collect_sub_objects(seen_objs)
80+        self._collect_sub_objects(seen_objs, pre_signal=signals.pre_delete)
81 
82         # Actually delete the objects.
83         delete_objects(seen_objs, using)
84diff --git a/django/db/models/query.py b/django/db/models/query.py
85index d9fbd9b..b9947b7 100644
86--- a/django/db/models/query.py
87+++ b/django/db/models/query.py
88@@ -438,7 +438,7 @@ class QuerySet(object):
89             # need to maintain the query cache on del_query (see #12328)
90             seen_objs = CollectedObjects(seen_objs)
91             for i, obj in izip(xrange(CHUNK_SIZE), del_itr):
92-                obj._collect_sub_objects(seen_objs)
93+                obj._collect_sub_objects(seen_objs, pre_signal=signals.pre_delete)
94 
95             if not seen_objs:
96                 break
97@@ -1308,11 +1308,6 @@ def delete_objects(seen_objs, using):
98             items.sort()
99             obj_pairs[cls] = items
100 
101-            # Pre-notify all instances to be deleted.
102-            for pk_val, instance in items:
103-                if not cls._meta.auto_created:
104-                    signals.pre_delete.send(sender=cls, instance=instance)
105-
106             pk_list = [pk for pk,instance in items]
107 
108             update_query = sql.UpdateQuery(cls)