python 如何避免再次触发Django信号?

mnemlml8  于 2023-11-15  发布在  Python
关注(0)|答案(1)|浏览(128)

我有这个信号,我希望它不要运行,一旦它已经检查了每个示例一次,目前它福尔斯陷入一个无限递归循环,因为它触发自己每次运行。

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

字符串
感谢任何帮助,如果答案比n^2简单,就可以加分。
编辑:这是表单模型本身:

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

aij0ehis

aij0ehis1#

我强烈建议 * 不要 * 将分数存储在form对象中。事实上,这不仅可以避免信号的问题:我们可以在数据库端更有效地这样做,并且只有当我们 * 需要 * 分数时,也使它更健壮。
信号通常是一个坏主意。事实上,信号可以规避,例如使用**.bulk_create(…)**[Django-doc]不会为创建的对象触发信号。还有很多情况下数据可以更改:创建记录,更新记录,删除记录。事实证明,即使在同一个数据库中,不容易。我总结了this article [django-antipatterns]中信号的一些问题。
因此,最好省略score

class QuestionForm(models.Model):
    questions = models.ManyToManyField(
        Question,
        related_name='questions'
    )
    # …
    # no score field
    pass

class Question(models.Model):
    points = models.IntegerField()

字符串
现在,如果我们想要Form s与相关Question s的相应得分,我们可以使用:用途:

from django.db.models import Sum

QuestionForm.objects.annotate(score=Sum('questions__points'))


QuerySet产生的Form s将有一个额外的属性.score,它将包含 relatedQuestion s的.points之和。如果查询集被过滤掉,它也不会聚合我们不需要的查询集,因为聚合是由数据库完成的,这通常是非常有效的。

备注:**related_name=…**参数[Django-doc]是 reverse 中关系的名称,因此在这种情况下从Question模型到QuestionForm模型。因此,将其命名为与正向关系相同没有多大意义。因此,您可能需要考虑将questions关系重命名为forms

相关问题