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

Django如何处理库存为1时两名用户同时购买实物商品的问题

Django电商库存并发校验与购物车自动清理实现方案

一、核心设计思路

两个需求对应两个校验节点即可实现,核心是通过数据库行锁解决并发购买的竞态问题,避免超卖:

  1. 提交订单/进入支付前做实时库存校验,拦截库存不足的结算请求
  2. 购物车页面加载时先做全量库存校验,自动清理无库存商品并提示用户

二、具体实现步骤

1. 模型层基础准备

先定义商品、购物车核心模型字段,示例如下:

from django.db import models
from django.contrib.auth.models import User

# 商品模型
class Product(models.Model):
    name = models.CharField(max_length=100)
    stock = models.IntegerField(default=0) # 可售库存
    # 其余商品字段按需补充

# 购物车条目模型
class CartItem(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)
    add_time = models.DateTimeField(auto_now_add=True)

2. 进入支付环节的并发校验逻辑(解决第一个需求)

必须用select_for_update对商品行加排他锁,避免两个并发请求同时查询到剩余库存1都通过校验的问题,所有校验和库存扣减放在同一个事务中保证原子性,示例代码如下:

from django.db import transaction
from django.contrib import messages

@transaction.atomic
def create_order(request):
    # 获取用户本次选择结算的购物车条目ID列表
    settle_cart_ids = request.POST.getlist('cart_item_ids')
    cart_items = CartItem.objects.filter(user=request.user, id__in=settle_cart_ids)
    # 提取关联商品ID,加行锁锁定商品行,直到当前事务结束其他请求才能修改该商品数据
    product_ids = [item.product_id for item in cart_items]
    locked_products = Product.objects.select_for_update().filter(id__in=product_ids)
    product_map = {p.id: p for p in locked_products}

    for item in cart_items:
        current_product = product_map[item.product_id]
        # 任意商品库存不足直接终止结算,返回错误
        if current_product.stock < item.quantity:
            messages.error(request, f"商品【{current_product.name}】库存不足,无法进入支付流程")
            return redirect('cart_list_page')
        # 校验通过扣减库存
        current_product.stock -= item.quantity
        current_product.save(update_fields=['stock'])
    
    # 后续创建待支付订单、跳转支付页逻辑按需补充
    return redirect('pay_page')

3. 购物车加载时自动清理逻辑(解决第二个需求)

用户登录后访问购物车页面时,先全量校验所有购物车商品库存,删除无库存商品后再返回页面,示例代码如下:

def cart_list_page(request):
    if not request.user.is_authenticated:
        return redirect('login_page')
    
    cart_items = CartItem.objects.filter(user=request.user).select_related('product')
    removed_product_names = []
    for item in cart_items:
        if item.product.stock < item.quantity:
            removed_product_names.append(item.product.name)
            item.delete()
    
    # 有被删除的商品则给用户提示
    if removed_product_names:
        messages.warning(request, f"以下商品因库存不足已从购物车移除:{','.join(removed_product_names)}")
    
    # 查询剩余有效购物车条目返回页面
    valid_cart_items = CartItem.objects.filter(user=request.user).select_related('product')
    return render(request, 'cart/list.html', {'cart_items': valid_cart_items})

三、优化补充点

  • 若业务存在秒杀等高并发场景,可将库存校验逻辑迁移至Redis实现,性能更高,普通电商场景上述数据库行锁方案完全可以满足需求
  • 建议给待支付订单增加超时自动取消逻辑,例如15分钟未支付自动回滚库存,避免用户占库存不付款导致其他用户无法购买
  • 库存扣减与订单创建必须放在同一个数据库事务中执行,避免出现库存扣减成功但订单创建失败的不一致问题

内容的提问来源于stack exchange,提问作者Gabriel Paschall

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 06:27:02