我有这两个基本模型,
class Product(models.Model):
title = models.CharField(max_length=40)
description = models.TextField(blank=True)
price = models.DecimalField(decimal_places=2, max_digits=7, default=0)
...
和
class Cart(models.Model):
user = models.ForeignKey(
User, null=True, blank=True, on_delete=models.CASCADE)
products = models.ManyToManyField(
Product, blank=True, through='CartItem')
total = models.DecimalField(default=0.00, max_digits=7, decimal_places=2)
def recalculate_and_save(self):
print("recalculate_and_save running")
total = 0
for ci in self.cartitem_set.all():
total += ci.product.price*ci.quantity
self.total = total
self.save()
,以及上面的多对多关系的助手模型,以说明数量:
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.SmallIntegerField(default=1)
我想实现的是每次添加或删除商品时自动计算购物车的总数。
@receiver(m2m_changed, sender=CartItem)
def m2m_changed_cart_receiver(sender, instance, action, *args, **kwargs):
print(f"m2m_changed received; action={action}, instance={instance}")
instance.recalculate_and_save()
然后我意识到,在日志中看到pre_save和postrongave动作,我可能做了一些错误的事情-在从父母的保存调用(两次)的函数中调用save,按照文档。第一个问题是,为什么它没有把我送入一个无限循环?第二个(可能更重要)—为什么我只看到接收函数在从购物车中删除商品时执行,而不是在添加商品时执行?删除是通过
cart.products.remove (product_obj)
,但通过
添加cart_obj.cartitem_set。添加(cart_item_obj散装= False)
,这可能与为什么删除会触发接收者,而添加不会有关。但这使问题更加复杂——将发送方设置为CartItem,我希望在产品上执行的删除会错过接收方,而不是直接对CartItem起作用的添加(尽管通过on_delete=CASCADE删除产品也会删除CartItem)。
修改ManyToManyField
(本例中为products
)时触发m2m_changed
信号
来自docs
:
Sent when a ManyToManyField is changed on a model instance
对于你的问题:
为什么它不让我进入无限循环?
这是因为recalculate_and_save
没有对m2m字段products
做任何事情,所以它不会再次触发m2m_changed
信号,避免了无限循环。
为什么我只看到接收函数在从购物车中删除商品时执行,而不是在添加商品时执行?
由于信号只来自于修改products
时,所以对cartitem_set
进行操作不会触发它。
所以你需要在修改cartitem_set
后手动调用recalculate_and_save
:
cart_obj.cartitem_set.add(cart_item_obj, bulk=False)
cart_obj.recalculate_and_save()