Send post_delete signals immediately
In a normal relational construct, if you're listening for an event that signals a child was deleted, you dont expect that the parent was deleted already. This change ensures that post_delete signals are fired immediately after objects are deleted in the graph.
This commit is contained in:
parent
4720117a31
commit
272de9eb6b
|
@ -75,7 +75,7 @@ class Collector(object):
|
||||||
self.using = using
|
self.using = using
|
||||||
# Initially, {model: set([instances])}, later values become lists.
|
# Initially, {model: set([instances])}, later values become lists.
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.field_updates = {} # {model: {(field, value): set([instances])}}
|
self.field_updates = {} # {model: {(field, value): set([instances])}}
|
||||||
# fast_deletes is a list of queryset-likes that can be deleted without
|
# fast_deletes is a list of queryset-likes that can be deleted without
|
||||||
# fetching the objects into memory.
|
# fetching the objects into memory.
|
||||||
self.fast_deletes = []
|
self.fast_deletes = []
|
||||||
|
@ -85,7 +85,7 @@ class Collector(object):
|
||||||
# should be included, as the dependencies exist only between actual
|
# should be included, as the dependencies exist only between actual
|
||||||
# database tables; proxy models are represented here by their concrete
|
# database tables; proxy models are represented here by their concrete
|
||||||
# parent.
|
# parent.
|
||||||
self.dependencies = {} # {model: set([models])}
|
self.dependencies = {} # {model: set([models])}
|
||||||
|
|
||||||
def add(self, objs, source=None, nullable=False, reverse_dependency=False):
|
def add(self, objs, source=None, nullable=False, reverse_dependency=False):
|
||||||
"""
|
"""
|
||||||
|
@ -262,6 +262,14 @@ class Collector(object):
|
||||||
self.data = SortedDict([(model, self.data[model])
|
self.data = SortedDict([(model, self.data[model])
|
||||||
for model in sorted_models])
|
for model in sorted_models])
|
||||||
|
|
||||||
|
def send_post_delete_signals(self, model, instances):
|
||||||
|
if model._meta.auto_created:
|
||||||
|
return
|
||||||
|
for obj in instances:
|
||||||
|
signals.post_delete.send(
|
||||||
|
sender=model, instance=obj, using=self.using
|
||||||
|
)
|
||||||
|
|
||||||
@force_managed
|
@force_managed
|
||||||
def delete(self):
|
def delete(self):
|
||||||
# sort instance collections
|
# sort instance collections
|
||||||
|
@ -300,13 +308,7 @@ class Collector(object):
|
||||||
query = sql.DeleteQuery(model)
|
query = sql.DeleteQuery(model)
|
||||||
pk_list = [obj.pk for obj in instances]
|
pk_list = [obj.pk for obj in instances]
|
||||||
query.delete_batch(pk_list, self.using)
|
query.delete_batch(pk_list, self.using)
|
||||||
|
self.send_post_delete_signals(model, instances)
|
||||||
# send post_delete signals
|
|
||||||
for model, obj in self.instances_with_model():
|
|
||||||
if not model._meta.auto_created:
|
|
||||||
signals.post_delete.send(
|
|
||||||
sender=model, instance=obj, using=self.using
|
|
||||||
)
|
|
||||||
|
|
||||||
# update collected instances
|
# update collected instances
|
||||||
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
||||||
|
|
|
@ -229,6 +229,18 @@ class DeletionTests(TestCase):
|
||||||
models.signals.post_delete.disconnect(log_post_delete)
|
models.signals.post_delete.disconnect(log_post_delete)
|
||||||
models.signals.post_delete.disconnect(log_pre_delete)
|
models.signals.post_delete.disconnect(log_pre_delete)
|
||||||
|
|
||||||
|
def test_relational_post_delete_signals_happen_before_parent_object(self):
|
||||||
|
def log_post_delete(instance, **kwargs):
|
||||||
|
self.assertTrue(R.objects.filter(pk=instance.r_id))
|
||||||
|
|
||||||
|
models.signals.post_delete.connect(log_post_delete, sender=S)
|
||||||
|
|
||||||
|
r = R.objects.create(pk=1)
|
||||||
|
S.objects.create(pk=1, r=r)
|
||||||
|
r.delete()
|
||||||
|
|
||||||
|
models.signals.post_delete.disconnect(log_post_delete)
|
||||||
|
|
||||||
@skipUnlessDBFeature("can_defer_constraint_checks")
|
@skipUnlessDBFeature("can_defer_constraint_checks")
|
||||||
def test_can_defer_constraint_checks(self):
|
def test_can_defer_constraint_checks(self):
|
||||||
u = User.objects.create(
|
u = User.objects.create(
|
||||||
|
|
Loading…
Reference in New Issue