Django中实现交易录入时自动扣减商品库存的技术咨询
Django中实现交易录入时自动扣减商品库存的技术咨询
嘿,我看了你这段Django模型代码,发现几个需要调整的地方,刚好能解决你自动扣减库存的需求,咱们一步步来梳理:
先理清模型设计的小问题
你的Transaction模型里用了ManyToManyField关联Product,但结合你“交易录入时扣减对应商品库存”的需求,这里有点小矛盾:如果一个交易关联多个商品,那quantity字段没法对应到每个商品的购买数量。所以我分两种常见场景来给你解决方案:
场景1:一个交易对应单个商品
这种情况最贴合你代码里quantity和total的设计,只需要把ManyToManyField改成ForeignKey就行,然后在save方法里添加库存扣减逻辑:
from django.db import models from django.contrib.auth.models import User class Product(models.Model): name = models.CharField(max_length=150) stock = models.IntegerField() cogs = models.IntegerField() def __str__(self): return f"{self.name}" class Transaction(models.Model): customer = models.ForeignKey(User, on_delete=models.CASCADE) # 替换ManyToMany为ForeignKey,对应单个商品 product = models.ForeignKey(Product, on_delete=models.CASCADE) purchase_date = models.DateField(auto_now_add=True) quantity = models.IntegerField() total = models.IntegerField(blank=True) def save(self, *args, **kwargs): # 先计算交易总价 self.total = self.quantity * self.product.cogs # 用原子操作+行锁避免并发超卖问题 with models.transaction.atomic(): # 锁定当前商品,防止其他请求同时修改库存 target_product = Product.objects.select_for_update().get(id=self.product.id) # 先检查库存是否足够 if target_product.stock >= self.quantity: target_product.stock -= self.quantity target_product.save() else: raise ValueError("库存不足,无法完成本次交易") # 最后调用父类的save方法保存交易记录 super().save(*args, **kwargs) def __str__(self): return f"{self.customer}"
场景2:一个交易对应多个商品(多商品下单)
如果你的需求是支持一个交易买多个不同商品,每个商品有独立的购买数量,那需要自定义ManyToMany的中间表来存储每个商品的购买数量,代码调整如下:
from django.db import models from django.contrib.auth.models import User class Product(models.Model): name = models.CharField(max_length=150) stock = models.IntegerField() cogs = models.IntegerField() def __str__(self): return f"{self.name}" # 自定义中间表,记录交易中每个商品的购买数量 class TransactionProduct(models.Model): transaction = models.ForeignKey('Transaction', on_delete=models.CASCADE) product = models.ForeignKey(Product, on_delete=models.CASCADE) quantity = models.IntegerField() class Transaction(models.Model): customer = models.ForeignKey(User, on_delete=models.CASCADE) # 通过through参数关联自定义中间表 product = models.ManyToManyField(Product, through=TransactionProduct) purchase_date = models.DateField(auto_now_add=True) total = models.IntegerField(blank=True) def save(self, *args, **kwargs): total_amount = 0 # 同样用原子操作保证数据一致性 with models.transaction.atomic(): # 遍历交易关联的所有商品及对应数量 for tp in self.transactionproduct_set.all(): target_product = Product.objects.select_for_update().get(id=tp.product.id) if target_product.stock >= tp.quantity: target_product.stock -= tp.quantity target_product.save() total_amount += tp.quantity * target_product.cogs else: raise ValueError(f"商品【{target_product.name}】库存不足,无法完成交易") # 计算交易总金额 self.total = total_amount # 保存交易记录 super().save(*args, **kwargs) def __str__(self): return f"{self.customer}"
几个关键注意点
- 并发安全:一定要用
transaction.atomic()和select_for_update(),不然高并发场景下会出现多个请求同时扣减同一件商品库存,导致库存负数或者超卖的问题。 - 库存校验:每次扣减前一定要检查库存是否足够,避免出现负库存的情况。
- 后台录入适配:如果是用Django Admin录入交易,多商品场景需要自定义Admin表单来支持中间表的数量录入,不然默认Admin没法直接设置每个商品的购买数量。
备注:内容来源于stack exchange,提问作者Gabriel Siahaan
相关产品推荐
相关产品推荐

