Django电商购物车功能实现与优化实践
2026/7/22 12:41:15 网站建设 项目流程

1. 项目概述

电商平台的购物车功能是连接商品浏览与订单支付的关键环节,直接影响用户转化率。基于Django框架实现购物车页面的商品增删改查功能,需要兼顾前后端交互的流畅性与数据一致性。本文将详细拆解从商品加入购物车到批量操作的全流程实现方案。

2. 技术架构设计

2.1 模型层设计

购物车系统采用经典的主从表结构:

class CartItem(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cart_items') sku = models.ForeignKey('products.SKU', on_delete=models.CASCADE) quantity = models.PositiveIntegerField(default=1) is_checked = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = [['user', 'sku']] # 避免重复添加

关键设计考量:

  1. 与用户表建立外键关联,实现多用户隔离
  2. 关联SKU最小库存单位,记录商品规格信息
  3. 使用quantity字段存储购买数量,设置正整数校验
  4. 通过is_checked字段实现商品勾选状态管理
  5. 联合唯一索引防止同一商品重复添加

2.2 接口路由规划

采用RESTful风格设计URL结构:

urlpatterns = [ path('', views.cart_list, name='cart_list'), path('add/', views.cart_add, name='cart_add'), path('update/<int:item_id>/', views.cart_update, name='cart_update'), path('delete/<int:item_id>/', views.cart_delete, name='cart_delete'), path('batch_delete/', views.cart_batch_delete, name='cart_batch_delete'), path('check/<int:item_id>/', views.cart_check, name='cart_check'), path('check_all/', views.cart_check_all, name='cart_check_all'), ]

路由特点:

  • 使用动词前缀明确操作类型
  • 资源ID通过URL参数传递
  • 命名路由便于前端反向解析
  • 批量操作使用独立端点

3. 核心功能实现

3.1 商品加入购物车

@require_POST def cart_add(request): sku_id = request.POST.get('sku_id') quantity = int(request.POST.get('quantity', 1)) sku = get_object_or_404(SKU, pk=sku_id) if quantity > sku.stock: return JsonResponse({'ok': False, 'msg': f'库存不足(仅剩 {sku.stock} 件)'}) cart_item, created = CartItem.objects.get_or_create( user=request.user, sku=sku, defaults={'quantity': quantity} ) if not created: # 已存在则累加数量 new_quantity = cart_item.quantity + quantity if new_quantity > sku.stock: return JsonResponse({'ok': False, 'msg': f'库存不足(当前已有 {cart_item.quantity} 件)'}) cart_item.quantity = new_quantity cart_item.save() return JsonResponse({ 'ok': True, 'cart_count': request.user.cart_items.count() })

关键实现细节:

  1. 使用get_or_create实现幂等操作
  2. 库存校验前置,避免超卖
  3. 已存在商品自动数量累加
  4. 返回当前购物车商品总数用于更新UI

3.2 商品数量修改

@require_POST def cart_update(request, item_id): cart_item = get_object_or_404(CartItem, pk=item_id, user=request.user) action = request.POST.get('action') # 'increase'/'decrease'/'set' # 计算新数量 if action == 'increase': new_qty = cart_item.quantity + 1 elif action == 'decrease': new_qty = cart_item.quantity - 1 elif action == 'set': new_qty = int(request.POST.get('quantity')) # 边界校验 if new_qty < 1: return JsonResponse({'ok': False, 'msg': '数量不能小于1'}) if new_qty > cart_item.sku.stock: return JsonResponse({'ok': False, 'msg': '超过库存上限'}) # 更新数据 cart_item.quantity = new_qty cart_item.save() # 重新计算总价 checked_items = request.user.cart_items.filter(is_checked=True) total_price = sum(item.sku.price * item.quantity for item in checked_items) return JsonResponse({ 'ok': True, 'new_quantity': new_qty, 'subtotal': cart_item.sku.price * new_qty, 'total_price': total_price })

注意事项:

  1. 支持三种操作方式:递增/递减/直接设置
  2. 数量修改需同步更新小计和总价
  3. 边界值校验确保数据合法性
  4. 使用select_related优化关联查询

4. 前端交互实现

4.1 购物车列表页

采用Bootstrap5构建响应式表格:

<table class="table table-hover"> <thead> <tr> <th><input type="checkbox" id="check-all"></th> <th>商品信息</th> <th>单价</th> <th>数量</th> <th>小计</th> <th>操作</th> </tr> </thead> <tbody> {% for item in cart_items %} <tr id="cart-item-{{ item.id }}"> <td><input type="checkbox" class="item-checkbox" >function postJSON(url, data, callback) { fetch(url, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams(data) }) .then(response => response.json()) .then(callback) .catch(console.error); } // 示例:数量增加操作 document.querySelectorAll('.btn-plus').forEach(btn => { btn.addEventListener('click', function() { const itemId = this.dataset.itemId; postJSON(`/cart/update/${itemId}/`, {action: 'increase'}, data => { if (data.ok) { updateUI(data); // 更新界面元素 } else { alert(data.msg); } }); }); });

5. 性能优化实践

5.1 数据库查询优化

  1. 使用select_related预取外键关联:
CartItem.objects.filter(user=request.user).select_related('sku')
  1. 批量操作使用事务保证原子性:
@transaction.atomic def cart_batch_delete(request): item_ids = request.POST.getlist('item_ids[]') CartItem.objects.filter(pk__in=item_ids, user=request.user).delete() # ...

5.2 前端性能提升

  1. 事件委托减少监听器数量:
document.getElementById('cart-table').addEventListener('click', function(e) { if (e.target.classList.contains('btn-plus')) { // 处理增加操作 } });
  1. 防抖处理数量输入:
const debounce = (fn, delay) => { let timer; return function() { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, arguments), delay); }; }; quantityInput.addEventListener('input', debounce(function() { // 发送更新请求 }, 500));

6. 异常处理与边界情况

6.1 库存超卖防护

采用双层校验机制:

  1. 前端实时显示可用库存
  2. 后端提交时再次校验:
if new_quantity > cart_item.sku.stock: return JsonResponse({'ok': False, 'msg': '库存不足'})

6.2 并发控制

使用select_for_update锁定记录:

with transaction.atomic(): cart_item = CartItem.objects.select_for_update().get(pk=item_id) # 处理数量变更

7. 扩展功能实现

7.1 全选/反选逻辑

@require_POST def cart_check_all(request): checked = request.POST.get('checked') == 'true' request.user.cart_items.update(is_checked=checked) total_price = sum( item.sku.price * item.quantity for item in request.user.cart_items.all() ) if checked else 0 return JsonResponse({ 'ok': True, 'total_price': total_price, 'checked_count': request.user.cart_items.count() if checked else 0 })

7.2 批量删除实现

@require_POST def cart_batch_delete(request): item_ids = request.POST.getlist('item_ids[]') if item_ids: CartItem.objects.filter(pk__in=item_ids, user=request.user).delete() return JsonResponse({ 'ok': True, 'msg': f'已删除{len(item_ids)}件商品' })

8. 项目部署建议

  1. 缓存策略:
from django.core.cache import cache def get_cart_count(user): cache_key = f'cart_count_{user.id}' count = cache.get(cache_key) if count is None: count = user.cart_items.count() cache.set(cache_key, count, timeout=60*5) # 5分钟缓存 return count
  1. 异步任务处理:
from celery import shared_task @shared_task def async_update_cart(item_id, quantity): try: item = CartItem.objects.get(pk=item_id) item.quantity = quantity item.save() except CartItem.DoesNotExist: pass

9. 测试用例设计

9.1 单元测试示例

class CartTests(TestCase): def setUp(self): self.user = User.objects.create_user(username='test', password='test') self.sku = SKU.objects.create(name="Test", price=100, stock=10) def test_add_item(self): self.client.login(username='test', password='test') response = self.client.post('/cart/add/', {'sku_id': self.sku.id}) self.assertEqual(response.json()['ok'], True) self.assertEqual(self.user.cart_items.count(), 1) def test_stock_check(self): item = CartItem.objects.create(user=self.user, sku=self.sku, quantity=5) response = self.client.post('/cart/update/1/', {'action': 'set', 'quantity': 15}) self.assertEqual(response.json()['ok'], False)

10. 项目经验总结

  1. 性能优化点:
  • 使用prefetch_related优化多对多关系查询
  • 添加数据库索引加速查询:
class Meta: indexes = [ models.Index(fields=['user', 'is_checked']), ]
  1. 前端交互优化:
  • 添加加载状态提示
  • 实现动画过渡效果
  • 本地存储临时数据
  1. 安全防护措施:
  • CSRF令牌校验
  • 权限验证装饰器
  • 请求方法限制

实际开发中发现的问题及解决方案:

  1. 移动端输入框体验问题:改用触摸友好的数字选择器
  2. 高并发下库存不一致:引入Redis原子计数器
  3. 批量操作超时问题:分页处理大数据量

后续优化方向:

  1. 持久化未登录用户的购物车
  2. 加入商品价格变动提醒
  3. 实现购物车商品推荐功能

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询