我有一个一对多关系,并且我想自动删除多上最后一个引用对象之后的一端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_collect
在 Bar
被删除后被调用.
接下来的问题:
- 有没有更好的方法来收集一对多关系的一端垃圾?
- 如果没有,我应该更改什么才能使用 QuerySet.delete?
最佳答案
通过检查 delete()
中的代码里面django/db/models/deletion.py
,我找到了QuerySet.delete
批量删除收集的实例并THEN触发器post_delete
对于那些已删除的实例。如果删除Bar()
在第一post_delete
呼吁第一个删除Foo()
例如,稍后post_delete
的Foo()
实例将失败,因为 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/