gpt4 book ai didi

python - Django 中的垃圾收集对象

转载 作者:行者123 更新时间:2023-12-01 06:01:26 24 4
gpt4 key购买 nike

我有一个一对多关系,并且我想自动删除上最后一个引用对象之后的端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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com