首页 > 解决方案 > 更新 m2m 字段中的对象时如何使用 django 信号更新模型字段?

问题描述

我正在尝试在更新 OrderItem 上的数量时更新订单总额。为此,我使用 django 信号 m2m 更改 post_add 或 post_remove 操作。这是我的模型:

class Item(models.Model):
    name = models.CharField(max_length=20, unique=True)
    price = models.DecimalField(max_digits=8, decimal_places=2)


class Order(models.Model):
    order_item = models.ManyToManyField('OrderItem')
    total = models.DecimalField(max_digits=8, decimal_places=2, default=0.0)


class OrderItem(models.Model):
    item = models.ForeignKey(Item, on_delete=models.PROTECT)
    quantity = models.IntegerField()
    total = models.DecimalField(max_digits=8, decimal_places=2, default=0.0)

OrderItem 总更新信号

def pre_save_order_tem_receiver(sender, instance, *args, **kwargs):
    """Receiver for updating total of OrderItem"""
    total = instance.item.price * instance.quantity
    instance.total = total


pre_save.connect(pre_save_order_tem_receiver, sender=OrderItem)

m2m 改变信号

def m2m_changed_order_item_receiver(sender, instance, action, *args, **kwargs):
    """Receiver for updating total of Order through OrderItem"""
    if action in ["post_add", "post_remove"]:
        order_items = instance.order_item.all()
        total = 0
        for order_item in order_items:
            total += order_item.item.price * order_item.quantity
        instance.total = total
        instance.save()


m2m_changed.connect(m2m_changed_order_item_receiver, sender=Order.order_item.through)

测试用例:

    def test_updating_order_item_quantity_in_order(self):
        order_item1, order_item1_data = create_sample_order_item(
            item=self.item1,
            quantity=2,
            data_only=False
        )

        order_item2, order_item2_data = create_sample_order_item(
            item=self.item2,
            quantity=2,
            data_only=False
        )

        order, _ = create_sample_order(
            order_items=[order_item1_data, order_item2_data],
            data_only=False
        )

        order_items = order.order_item.all()

        for order_item in order_items:
            if order_item == order_item2:
                order_item2.quantity = 10
                order_item2.save()
        order.save()

        # update
        order_item2_total = order_item2.item.price * 10

        # works completly fine but i'm searching for alternative method using signal
        # order.order_item.remove(order_item2)
        # order.order_item.add(order_item2)

        order.refresh_from_db()
        order_item1.refresh_from_db()
        order_item2.refresh_from_db()

        # check weather OrderItem total is updated or not
        self.assertEqual(order_item2.total, order_item2_total)

        # fails here
        self.assertEqual(order.total, order_item1.total + order_item2.total)

当我第一次从 Order 中删除 OrderItem 对象时,它工作得很好,更新并添加它。IE

# remove order_item
order.order_item.remove(order_item2)

# perform update 
order_item.total = ......

# add back
order.order_item.add(order_item2)

除了使用信号删除,更新和添加之外,还有其他可靠的方法吗?

更新:

根据Muhammad Ihfazhillah提供的线索,我已经实现了部分工作代码,该代码只能在管理面板中工作。

def listen_order_item_change(sender, instance, **kwargs):
    orders = instance.order_set.all()
    for order in orders:
        # get all order items
        order_items = order.order_item.all()

        total = 0
        # find total for order
        for order_item in order_items:
            total += order_item.total
        order.total = total

        # save order
        order.save()
    # instance.save() -> causes RecursionError

post_save.connect(listen_order_item_change, sender=OrderItem)

标签: djangodjango-modelsmany-to-manysignals

解决方案


我认为您需要收听post_save信号才能执行此操作,因为您更新了单个对象。

m2m_changed'post_add和信号仅在模型关系中添加或删除某些内容post_remove时才发送。

post_save这是在 OrderItem 实例上使用的片段。

def listen_order_item_change(sender, instance, **kwargs):
    # get all orders
    orders = instance.order_set.all()
    for order in orders:
        # update the each total for each order here

post_save.connect(listen_order_item_change, sender=OrderItem)

推荐阅读