python - Django 中的垃圾收集对象

标签 python django garbage-collection django-queryset django-signals

我有一个一对多关系,并且我想自动删除上最后一个引用对象之后的端em> 边已被删除。也就是说我要进行垃圾回收,或者说是进行一种反向级联操作。

我尝试使用 Django 的 post_delete 信号来解决这个问题。这是我正在尝试做的事情的简化示例:

模型.py

class Bar(models.Model):
    j = models.IntegerField()
    # implicit foo_set

class Foo(models.Model):
    i = models.IntegerField()
    bar = models.ForeignKey(Bar)

def garbage_collect(sender, instance, **kwargs):
    # Bar should be deleted after the last Foo.
    if instance.bar.foo_set.count() == 0:
        instance.bar.delete()

post_delete.connect(garbage_collect, Foo)

这在使用 Model.delete 时有效,但使用 QuerySet.delete 时会严重损坏。

测试.py

class TestGarbageCollect(TestCase):
    # Bar(j=1)
    # Foo(bar=bar, i=1)
    # Foo(bar=bar, i=2)
    # Foo(bar=bar, i=3)
    fixtures = ['db.json']

    def test_separate_post_delete(self):
        for foo in Foo.objects.all():
            foo.delete()
        self.assertEqual(Foo.objects.count(), 0)
        self.assertEqual(Bar.objects.count(), 0)

这工作得很好。

tests.py 继续

    def test_queryset_post_delete(self):
        Foo.objects.all().delete()
        self.assertEqual(Foo.objects.count(), 0)
        self.assertEqual(Bar.objects.count(), 0)

这会在第二次发出信号时中断,因为 Django's documentation表示,QuerySet.delete 立即应用,并且 instance.bar.foo_set.count() == 0 在第一次发出信号时就已经为 true。仍在阅读docs , QuerySet.delete 将为每个删除的对象发出 post_delete 信号,并且 garbage_collectBar 被删除后被调用.

接下来的问题:

  1. 有没有更好的方法来收集一对多关系的一端垃圾?
  2. 如果没有,我应该更改什么才能使用 QuerySet.delete?

最佳答案

通过检查 delete() 中的代码里面django/db/models/deletion.py ,我找到了QuerySet.delete批量删除收集的实例并THEN触发器post_delete对于那些已删除的实例。如果删除Bar()在第一post_delete呼吁第一个删除Foo()例如,稍后post_deleteFoo()实例将失败,因为 Bar()他们指向的内容已被删除。

这里的关键是 Foo()具有相同条形的 s 并不指向相同的 Bar()例如,该栏被过早删除。那么我们就可以

  • 直接try...except查找instance.bar

    def garbage_collect(sender, instance, **kwargs):
        try:
            if instance.bar.foo_set.exists():
                instance.bar.delete()
        except Bar.DoesNotExist:
            pass
    
  • 预加载 Bar()对于每个实例以避免上述异常

    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()
    

以上两种解决方案都具有额外的作用 SELECT查询。更优雅的方式可能是

  • 删除Bar始终在garbage_collect或者稍后手动(如果可以的话):

    Bar.objects.filter(foo__isnull=True).delete()
    
  • garbage_collect ,记录Bar()的删除计划而不是删除,而是一些引用计数标志或排队任务。

关于python - Django 中的垃圾收集对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10223743/

相关文章:

python - Python 2.6.6应用程序的Scapy throw 语法错误

python - Tensorflow 网格 LSTM RNN TypeError

python - 我怎样才能使用 tensorflow 对象检测来只检测人?

python - Django:某些页面的 CSS 未扩展?

Java内存泄漏示例

java - 意外的 Full GC(符合人体工程学)

Python登录邮件服务器认证错误

django - 如何解决涉及 haystack 的循环导入?

django - 在 nginx 上部署 django channel

memory-management - 编译时如何实现引用计数器?