Я пытаюсь обновить сумму заказа при обновлении количества в 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 из Order, обновляю и добавляю его. т.е.
# 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)