【问题标题】:How to update model field using django signals when updating object in m2m field?更新 m2m 字段中的对象时如何使用 django 信号更新模型字段?
【发布时间】:2020-08-13 15:04:45
【问题描述】:

我正在尝试在更新 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 对象,更新并添加它时,它工作得很好。即

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

【问题讨论】:

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


    【解决方案1】:

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

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

    这是要在OrderItem模型中定义的更新方法的sn-p。

      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 信号及其接收器的 sn-p 可以是

    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)
    

    【讨论】:

    • 它会更新订单项目的总数,这不是我要问的目的。
    【解决方案2】:

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

    m2m_changedpost_addpost_remove 信号仅在模型关系中添加或删除某些内容时调度。

    这是在 OrderItem 实例上使用 post_save 的 sn-p。

    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)
    

    【讨论】:

    • 不要忘记将订单实例保存在for循环块中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    • 2021-11-19
    • 2016-03-12
    • 2021-06-08
    • 1970-01-01
    • 2011-07-27
    相关资源
    最近更新 更多