在Django中进行所有更改后,如何仅触发一次m2m_changed信号?

时间:2019-06-01 15:06:33

标签: django django-models many-to-many django-orm

我的项目中有以下模型:

class Topping(models.Model):
    name = models.CharField(max_length=255)

class Pizza(models.Model):
    name = models.CharField(max_length=255)
    toppings = models.ManyToManyField(Topping, blank=True)

    def save(self, *args, **kwargs):
        print('Saving...')
        if self.pk:
            for topping in self.toppings.all():
                print(topping.name)

        super(Pizza, self).save(*args, **kwargs)

def toppings_changed(sender, instance, **kwargs):
    instance.save()

m2m_changed.connect(toppings_changed, sender=Pizza.toppings.through)

基本上,只要更改toppings,就会触发信号。信号的全部作用就是调用Pizza对象的save方法。无论如何,可以说我有三个对象:

pizza = Pizza.objects.get(pk=1) # Number of toppings is 0
topping1 = Topping.objects.get(pk=1)
topping2 = Topping.objects.get(pk=2)

现在,我要为披萨设置两个浇头。我使用以下代码执行此操作:

pizza = Pizza.objects.get(pk=1)
pizza.toppings.set([1, 2])

浇头的设置正确,但是,由于发生了两次更改,因此两次调用了披萨的save方法,因为两次调用了m2m_changed信号。在所有更改都已提交之后,我怎么只能调用一次?为了明确起见,我希望同时添加两个浇头,但是在所有更改结束时,我只希望触发一次信号。感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

java.util.Date信号有点奇怪,它被调用了两次:一次是用toString()的动作,一次是用m2m_changed的动作(请参阅docs )。由于您要为任何操作调用pre_add,它将最终被调用两次,因此post_add将被调用两次。

您似乎对toppings_changed()感兴趣,所以我会做类似的事情:

save()

在这里,无论您要传递给post_add的参数多少,@receiver(m2m_changed, sender=Pizza.toppings.through) def toppings_changed(sender, instance, action, **kwargs): if action == "post_add": instance.save() 都只能被调用一次。请注意,如果您使用接收器装饰器,则无需执行instance.save()

以上内容也适用于set()m2m_changed.connect(...),因此,如果要在删除后打印列表,则需要添加更多逻辑以检查pre/post_remove是否有效。

最后,以防万一,如果您不知道,pre/post_clear将用您提供的两个{action == "post_remove"set([1, 2]) 替换所有浇头。如果您只是想将浇头添加到现有组合中,则可以使用pk=1

希望这会有所帮助!