[Answered ]-Garbage collecting objects in Django

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 of instance.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 exception

    def 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 in garbage_collect or manually later, if you can:

    Bar.objects.filter(foo__isnull=True).delete()
    
  • In garbage_collect, record the deletion plan for Bar() instead of deleting, to some ref-count flag or queued tasks.

👤okm

0👍

I ques you can override the model’s method delete, find the related objects and delete them too.

Leave a comment