在更新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)

当我第一次从订单中删除OrderItem对象,更新并添加它时,它工作得非常好

# remove order_item
order.order_item.remove(order_item2)
# perform update 
order_item.total = ......
# add back
order.order_item.add(order_item2)

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

更新:

根据穆罕默德·伊赫法日拉提供的线索,我已经实现了部分工作代码,它只能从管理面板中工作。

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)

我认为您需要监听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)

我认为这可以通过简单地触发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

最后,使用postrongave信号及其接收器的片段可以是

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)

最新更新