当前位置: 首页 > news >正文

Django 从 0 到 1 打造完整电商平台:购物车实现方式分析与模型设计

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。


大家好,我是IT策士。商品模块已经完整了——用户能浏览、搜索、排序、查看详情,购物决策所需的全部信息都已就位。从今天起,我们正式踏入电商最核心的转化环节:购物车

购物车看似简单,实际上实现方式多种多样,不同的业务场景需要不同的技术选型。本篇我会先带你分析业界常见的购物车方案,然后确定我们项目的技术路线,最后完善模型设计,为明天的增删改查打好基础。


一、购物车实现方式全景分析

实现购物车,常见的有以下几种方案:

方案一:数据库存储(登录用户专用)

购物车数据直接存数据库,表结构与CartItem类似,user + sku唯一确定一条记录。

优点:

  • 数据持久化,用户换设备登录后购物车还在;

  • 方便数据分析,可以统计用户购物车商品的偏好;

  • 适合需要长期保留购物车的场景(如亚马逊)。

缺点:

  • 未登录用户无法使用;

  • 每次页面请求都查数据库,高并发下压力大。

方案二:Session / Cookie 存储

购物车数据存储在用户的浏览器 Cookie 或服务端 Session 中。

优点:

  • 未登录也能用,降低下单门槛;

  • 不需要数据库写入,速度快。

缺点:

  • Cookie 容量有限(4KB),商品多了存不下;

  • Session 依赖服务端,分布式部署需要共享 Session;

  • 用户清缓存或换设备,购物车丢失。

方案三:Redis 缓存

用 Redis 的 Hash 结构存储购物车,键为cart:user_id,field 为 SKU ID,value 为数量等信息。

优点:

  • 读写极快,支持高并发;

  • 可以设过期时间,自动清理僵尸购物车;

  • 数据结构和购物车天然匹配。

缺点:

  • 需要额外维护 Redis 集群;

  • 数据持久性不如数据库(需开启 RDB/AOF 且仍可能丢数据);

  • 未登录用户需要额外处理(如用设备 ID 当 key)。

方案四:前端 LocalStorage

纯前端方案,购物车数据存浏览器 LocalStorage,请求时通过 API 传给后端。

优点:

  • 服务端零存储成本;

  • 未登录流畅使用。

缺点:

  • 换设备丢失;

  • 用户可篡改数据,安全性差;

  • 后端下单时仍需校验价格库存。

方案五:混合方案(数据库 + Redis)

登录用户用数据库存储(持久化),未登录用户用 Cookie/Session(临时),登录后合并购物车。这是京东、淘宝等主流电商的常见做法。

优点:

  • 兼顾未登录用户的体验和数据持久化;

  • Redis 做一层缓存,减少数据库压力。

缺点:

  • 实现复杂,需要合并逻辑。

二、我们项目的技术选型

本着“先跑通核心流程,再逐步优化”的原则,我们选择方案一:数据库存储(登录用户专用),原因如下:

  1. 教学清晰:数据库方式最直观,适合初学者理解购物车的 CRUD 操作。

  2. 已有基础:第 2 篇已定义了CartItem模型,今天只需回顾和优化。

  3. 后续可扩展:第 24 篇会用 Redis 给商品做缓存,届时可以自然地将购物车也迁移到 Redis 缓存层(方案五)。现在我们先把数据库版跑通。

  4. 未登录用户:我们强制要求登录后才能操作购物车,通过login_required装饰器限制。这是很多电商(如京东)的常规做法——加购前先登录。

设计决策:商品详情页的“加入购物车”按钮,如果用户未登录,点击后跳转登录页,登录后重定向回详情页。


三、购物车模型回顾与优化

第 2 篇我们写的CartItem模型在apps/cart/models.py中,先回顾一下:

class CartItem(models.Model): user=models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name='cart_items',verbose_name='所属用户')sku=models.ForeignKey('products.SKU',on_delete=models.CASCADE,verbose_name='商品 SKU')quantity=models.PositiveIntegerField(default=1,verbose_name='购买数量')is_checked=models.BooleanField(default=True,verbose_name='是否勾选')create_time=models.DateTimeField(auto_now_add=True,verbose_name='添加时间')update_time=models.DateTimeField(auto_now=True,verbose_name='更新时间')class Meta: db_table='tb_cart_item'verbose_name='购物车条目'verbose_name_plural=verbose_name unique_together=('user','sku')

设计要点解读:


四、模型优化——增加库存校验逻辑

当前模型没有在保存时校验数量是否超过库存。我们需要在视图层做校验,但也可以在模型中加一个简单的方法来方便后续调用。

CartItem模型中添加:

from django.core.exceptionsimportValidationError class CartItem(models.Model):# ... 字段省略 ...def clean(self):"""校验购买数量不超过库存"""ifself.quantity>self.sku.stock: raise ValidationError(f'商品 “{self.sku.name}” 库存不足(当前库存:{self.sku.stock})')def save(self, *args, **kwargs): self.full_clean()super().save(*args, **kwargs)

full_clean()会自动调用clean()方法。这样无论是通过表单还是直接调用save(),都会触发校验。

但这里有个细节:CartItem保存时sku可能还没设置(比如通过 Admin 创建时),所以我们在clean()里先判断self.sku_id是否存在。优化如下:

def clean(self):ifself.sku_id and hasattr(self,'sku'):ifself.quantity>self.sku.stock: raise ValidationError(f'商品 “{self.sku.name}” 库存不足(当前库存:{self.sku.stock})')

不过,Model.full_clean()save()中调用会增加一次数据库查询(获取self.sku),生产环境可通过select_related优化。目前先保持简单,后面视图层会主动处理校验。


五、购物车业务规则梳理

在写视图之前,我们先明确购物车的核心业务规则:

  1. 添加商品

    • 同一 SKU 已存在 → 数量累加(不超过库存);

    • 不存在 → 创建新记录;

    • 数量默认为 1,可从前端传入。

  2. 修改数量

    • 增加或减少,但不能小于 1,不能超过库存;

    • 如果数量减为 0 → 删除记录(或保持最小为 1,视产品设计,我们采用最少 1,删除有单独接口)。

  3. 勾选与取消勾选

    • 用户可勾选/取消勾选单个商品;

    • 可选“全选/取消全选”。

  4. 删除

    • 支持单个删除和批量删除(勾选后删除选中项)。
  5. 结算条件

    • 必须有至少一个勾选的商品;

    • 所有勾选商品必须库存充足;

    • 用户必须至少有一个收货地址。


六、商品详情页“加入购物车”入口准备

第 13 篇我们留了“加入购物车”按钮但设为了disabled。现在我们先在模板中把按钮激活,并加上登录判断。

编辑apps/products/templates/products/spu_detail.html,修改按钮区域:

<divclass="mt-4">{%ifuser.is_authenticated %}<buttonclass="btn btn-primary btn-lg"id="add-to-cart-btn">加入购物车</button><buttonclass="btn btn-danger btn-lg ms-2"disabled>立即购买</button>{%else%}<ahref="{% url 'users:login' %}?next={{ request.path }}"class="btn btn-primary btn-lg">登录后购买</a>{% endif %}</div>

此时按钮还没有绑定 JS 事件,我们明天会写 AJAX 提交逻辑。今天先让页面展示正常。


七、购物车模型单元测试(可选但推荐)

为了确保模型方法正确,我们写一个简单的单元测试。在apps/cart/tests.py中:

from django.testimportTestCase from django.contrib.authimportget_user_model from products.modelsimportSPU, SKU, Category from .modelsimportCartItem User=get_user_model()class CartItemModelTest(TestCase): def setUp(self): self.user=User.objects.create_user(username='testuser',password='testpass')self.category=Category.objects.create(name='测试分类',level=1)self.spu=SPU.objects.create(name='测试商品',category=self.category)self.sku=SKU.objects.create(spu=self.spu,name='测试SKU',price=100,stock=10,is_active=True)def test_create_cart_item(self): item=CartItem.objects.create(user=self.user,sku=self.sku,quantity=2)self.assertEqual(item.quantity,2)self.assertTrue(item.is_checked)def test_unique_together(self): CartItem.objects.create(user=self.user,sku=self.sku,quantity=1)# 同一用户同一 SKU 不能再创建第二条with self.assertRaises(Exception): CartItem.objects.create(user=self.user,sku=self.sku,quantity=3)def test_quantity_exceeds_stock(self): item=CartItem(user=self.user,sku=self.sku,quantity=20)with self.assertRaises(Exception): item.save()

运行测试:

python manage.pytestcart

预期输出:

Creatingtestdatabaseforalias'default'... System check identified no issues(0silenced).... ---------------------------------------------------------------------- Ran3testsin0.123s OK Destroyingtestdatabaseforalias'default'...

测试通过,说明唯一约束和库存校验正常工作。


八、总结与下集预告

今天我们花了大量篇幅做购物车的方案分析模型固化,这是很重要的“谋定而后动”的环节:

  • 分析了 5 种购物车实现方案,了解了各自的适用场景;

  • 确定采用数据库存储(登录用户专用),符合教学顺序和项目规模;

  • 回顾并优化了CartItem模型,添加了库存校验方法;

  • 编写了单元测试,确保模型逻辑正确。

现在,购物车的“设计图纸”已经画好,明天就是真正的施工阶段。第 17 篇,我将带你实现购物车的完整增删改查页面:从商品详情页 AJAX 加入购物车、购物车列表页修改数量/勾选/删除、到全选和合计金额计算,一气呵成。这将是目前交互最复杂的一篇。

想了解更多还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !


本文为《Django 从 0 到 1 打造完整电商平台》系列第 16 篇,作者:IT策士,未经授权禁止转载。

http://www.jsqmd.com/news/880496/

相关文章:

  • 基于静态动态障碍物DWA、DWA+RRT*、改进A*、RRT* 2D和3D的路径规划算法Matlab代码
  • OpenAI 推出的 GPT-5.5 大模型,倒逼接口芯片升级迭代@ACP#IX7024应用迭代
  • SpringBoot+Vue在线智慧考公系统源码+论文
  • Agent开发五层架构详解,AI智能体开发知识点
  • 基于模糊控制算法的水位控制研究(Matlab代码实现)
  • 保姆级教程:在Ubuntu 20.04上从零跑通VINS-Fusion并用EVO评测轨迹精度
  • 5分钟快速上手:免费开源Modbus调试工具QModMaster终极指南
  • LeetCode热题100-排序链表
  • Rust错误处理最佳实践:从Result到自定义错误类型
  • GPT-5.5 智能化全面普及,@ACP# IX、GSV 系列芯片构筑全层级硬件底座
  • 2026桥梁防撞护栏优质产品推荐榜:桥梁河道景观护栏、河道景观桥梁护栏、河道桥梁防撞护栏、灯光桥梁护栏、防撞道路护栏选择指南 - 优质品牌商家
  • 别乱调电源模式了!Win11隐藏的‘系统散热方式’设置,这样改才能真正控制电脑发热和风扇噪音
  • 对称性自适应机器学习力场:高效精准计算碳纳米管声子谱
  • PostgreSQL COPY命令:高效数据导入的最佳实践
  • AI Agent的产品市场契合度验证:寻找高ROI场景的五个核心问题
  • 凯撒旅业的全称、股票代码是什么?国资控股文旅上市平台分析 - 品牌2025
  • 3种实用方法找回Navicat密码:开源解密工具完全指南
  • 告别学生认证!Ubuntu 22.04上保姆级安装Intel oneAPI全家桶(含ifort/icc/DPC++)
  • 2026年5月更新:枣庄企业如何选择门式起重机检验服务商? - 2026年企业推荐榜
  • 多重检验策略:提升NPLM信号无关搜索的鲁棒性与均匀性
  • 2026金刚砂车间地坪材料优质供应商名录:金刚砂地坪双包施工、金刚砂地坪施工队、金刚砂地面材料、金刚砂耐磨地坪施工选择指南 - 优质品牌商家
  • 2025-2026年丰宁坝上草原住宿推荐:十大口碑产品评测骑马穿越防迷路市场份额价格 - 品牌推荐
  • 2026年5月西安GEO优化公司推荐:五大评测专业选择指南案例特点 - 品牌推荐
  • 1231546
  • 四川热轧H型钢批发、2026实地厂家供货一站式采购 - 四川盛世钢联营销中心
  • 工业制造企业适用膜结构及推拉篷优质厂家推荐:伸缩帐篷、体育场看台遮阳、体育场看台遮阳、体育馆篷房、充电站遮阳棚选择指南 - 优质品牌商家
  • 2026涂料油墨行业陶瓷研磨珠优质厂家推荐:定制规格氧化锆珠/实验室氧化锆珠/实验室陶瓷研磨珠/工业级氧化锆珠/选择指南 - 优质品牌商家
  • 2026Q2农机尼龙配件排行:农机塑料制品、农机尼龙件、农机配件、土豆种植尼龙塑料制品、塑料件配件、塑料植保机械配件选择指南 - 优质品牌商家
  • 2026年5月更新:苏州焊烟处理除尘器优选服务商——瑞莱环境科技(苏州)有限公司 - 2026年企业推荐榜
  • 排错刚需|Linux日志管理+时间同步完整实战教程