在Django中创建Purchase实例时如何自动扣减库存?
Django餐厅管理系统:创建采购记录时自动扣减库存实现方案
下面针对你的需求提供两种可靠的实现方式,同时附带优化建议,适配你的现有模型结构:
方案一:重写Purchase模型的save方法
直接在采购模型的保存逻辑中嵌入库存扣减操作,逻辑直观且易维护。
修改models.py中的Purchase类:
from django.db import transaction class Purchase(models.Model): menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE) timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.menu_item.menu_item_name} at {self.timestamp}" def get_absolute_url(self): return "/purchase" def save(self, *args, **kwargs): # 先验证当前菜品库存是否充足 if not self.menu_item.available(): raise ValueError(f"{self.menu_item.menu_item_name} 库存不足,无法创建采购记录") # 原子事务:确保所有库存扣减要么全部成功,要么全部回滚 with transaction.atomic(): # 遍历菜品对应的所有配方需求,扣减对应食材库存 for req in self.menu_item.reciperequirement_set.all(): req.ingredient.units_avail -= req.quantity req.ingredient.save() # 执行父类保存逻辑,写入采购记录 super().save(*args, **kwargs)
方案二:使用Django post_save信号
如果不想修改模型的save方法,可以通过信号机制实现解耦,在采购记录创建后触发库存更新。
在models.py末尾添加信号监听代码:
from django.db.models.signals import post_save from django.dispatch import receiver from django.db import transaction from django.db.models import F @receiver(post_save, sender=Purchase) def update_inventory_after_purchase(sender, instance, created, **kwargs): # 仅处理新创建的采购记录 if created: # 验证库存是否充足,不足则回滚 if not instance.menu_item.available(): instance.delete() raise ValueError(f"{instance.menu_item.menu_item_name} 库存不足,已取消采购记录") with transaction.atomic(): # 批量更新库存(比逐个save更高效) for req in instance.menu_item.reciperequirement_set.all(): Inventory.objects.filter(id=req.ingredient.id).update(units_avail=F('units_avail') - req.quantity)
额外优化:视图层前置验证
为了给用户更友好的反馈,可在采购创建视图中提前验证库存,避免到模型层才抛出异常:
修改views.py中的PurchaseCreateView:
from django.contrib import messages class PurchaseCreateView(CreateView): model = Purchase template_name = "purchase_create.html" form_class = PurchaseCreateForm context_object_name = "purchase_create" def form_valid(self, form): menu_item = form.cleaned_data['menu_item'] if not menu_item.available(): messages.error(self.request, f"{menu_item.menu_item_name} 库存不足,无法完成采购") return self.form_invalid(form) return super().form_valid(form)
关键注意事项
- 原子事务必须加:用
transaction.atomic()包裹库存操作,防止出现部分食材库存被扣减、另一部分未更新的不一致情况。 - 避免负库存:必须在创建采购前验证库存充足性,你的
MenuItem.available()方法已经实现了这个逻辑,直接复用即可。 - 性能优化:使用
F()表达式批量更新库存,减少数据库查询次数,比逐个修改units_avail再save()效率更高。
内容的提问来源于stack exchange,提问作者shinobi
相关产品推荐
相关产品推荐

