You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 06:50:21