我有这个信号,我希望它在检查完每个实例一次后就不再运行,目前它陷入无限递归循环,因为它每次运行时都会触发自身。
from django.db.models.signals import (
post_save,
)
from django.dispatch import receiver
from app.models import (
QuestionForm,
Question,
)
@receiver(post_save, sender=form)
def get_max_score(
sender: form,
instance: form,
**kwargs: dict,
) -> None:
forms = form.objects.all()
for form in forms.iterator():
total = 0
questions = form.questions.all()
for item in questions.iterator():
total += item.points
form.set_score(total)
form.save()
如有任何帮助,我们将不胜感激,如果答案不那么复杂,将加分。
编辑:这是表单模型本身:
class QuestionForm(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
questions = models.ManyToManyField(
Question,
related_name='questions'
)
created_at = models.DateTimeField(
auto_now_add=True,
editable=False,
)
updated_at = models.DateTimeField(
auto_now=True,
editable=False,
)
max_score = models.IntegerField(
default=0,
)
def __str__(self):
return self.name
def get_score(self):
return self.max_score
def set_score(self, score):
self.max_score = score
我强烈建议不要将分数存储在
form
对象中。事实上,这不仅可以避免信号问题:我们可以在数据库端更有效地做到这一点,并且仅当我们需要分数时,并且还可以使其更加稳健。
.bulk_create(…)
[Django-doc] 不会为创建的对象触发信号。还有很多数据可能发生变化的场景:创建记录、更新记录、删除记录。事实证明,即使在同一个数据库上,保持相同的数据同步也不容易。
因此,最好省略
score
:
class Form(models.Model):
# …
# no score field
pass
class Question(models.Model):
form = models.ForeignKey(
Form, on_delete=models.CASCADE, related_name='questions'
)
points = models.IntegerField()
现在,如果我们想要
Form
以及相关 Question
的相应分数,我们可以使用:
from django.db.models import Sum
Form.objects.annotate(score=Sum('questions__points'))
由此 Form
产生的
QuerySet
将具有额外的属性
.score
,它将包含相关
.points
的 Question
之和。如果查询集被过滤掉,它也不会聚合我们不需要的查询集,并且由于聚合是由数据库完成的,因此通常非常有效。