2👍
By checking code in delete()
inside django/db/models/deletion.py
, I found the QuerySet.delete
deletes collected instances in batch and THEN trigger post_delete
for those deleted instances. If you delete Bar()
in the first post_delete
calling for the first deleted Foo()
instance, later post_delete
of Foo()
instances will be failed because the Bar()
which they point to has already been deleted.
The key here is that Foo()
s having same bar does not point to the same Bar()
instance, and the bar gets deleted too early. Then we could
-
straightly
try...except
the lookup ofinstance.bar
def garbage_collect(sender, instance, **kwargs): try: if instance.bar.foo_set.exists(): instance.bar.delete() except Bar.DoesNotExist: pass
-
preload
Bar()
for each instances to avoid the above exceptiondef test_queryset_post_delete(self): Foo.objects.select_related('bar').delete() def garbage_collect(sender, instance, **kwargs): if instance.bar.foo_set.exists(): instance.bar.delete()
Both of above solutions do extra SELECT
queries. The more graceful ways could be
-
Do the deletion of
Bar
always ingarbage_collect
or manually later, if you can:Bar.objects.filter(foo__isnull=True).delete()
-
In
garbage_collect
, record the deletion plan forBar()
instead of deleting, to some ref-count flag or queued tasks.
0👍
I ques you can override the model’s method delete, find the related objects and delete them too.
- [Answered ]-Django Time_Zone not saving correct values
- [Answered ]-How do I use TDD to create a database representation of existing objects?
- [Answered ]-IntegrityError: may not be NULL
- [Answered ]-Python regular expression not matching file contents with re.match and re.MULTILINE flag
- [Answered ]-Django1.8 cannot include css, js files in template