[在m2m字段中更新对象时如何使用Django信号更新模型字段?

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

我正在尝试根据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)

Testcase:

    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对象,更新并添加它时,它完全可以正常工作。即

# remove order_item
order.order_item.remove(order_item2)

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

# add back
order.order_item.add(order_item2)

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

django django-models many-to-many signals
2个回答
0
投票

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

[m2m_changedpost_addpost_remove信号仅在模型关系中添加或删除了某些东西时调度。

这里是在OrderItem实例上使用post_save的代码段。

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)

0
投票

我认为这可以通过简单地触发post_save信号来实现。

首先,您可以在OrderItem模型内添加一个方法来更新总数,该方法稍后可以从post_save信号接收器中调用。

这里是要在OrderItem模型中定义的更新方法的代码段。

  def update_total_price(self):
        """for updating the total_price of an ordered item"""
        quantity = Decimal(self.quantity)
        unit_price = self.item.price #for getting the unit price of the item
        new_total_price = quantity * unit_price
        self.total = new_total_price
        self.save()
        return new_total_price

最后是使用post_save信号及其接收者的代码段可以是]

def post_save_orderitem_total_receiver(sender,instance, created, *args, **kwargs):
    if created:
        instance.update_total_price()

post_save.connect(post_save_orderitem_total_receiver, sender=OrderItem)
© www.soinside.com 2019 - 2024. All rights reserved.