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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 09:44:33