如何避免重新触发 django 信号?

问题描述 投票:0回答:1

我有这个信号,我希望它在检查完每个实例一次后就不再运行,目前它陷入无限递归循环,因为它每次运行时都会触发自身。

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
python django django-models django-signals
1个回答
0
投票

我强烈建议不要将分数存储在

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
,它将包含 
相关
 
.pointsQuestion
 之和。如果查询集被过滤掉,它也不会聚合我们不需要的查询集,并且由于聚合是由数据库完成的,因此通常非常有效。

© www.soinside.com 2019 - 2024. All rights reserved.