1. 项目整体思路选型为什么我坚持用Django做购物商城说到购物商城系统很多初学者第一反应是“这东西淘宝京东不是早就做完了吗自己再做一遍有什么意义”。但只要你真正动手写过一次就会发现商城系统是Web开发里最典型的综合性练兵场它牵扯用户认证、商品建模、购物车状态管理、订单事务、支付回调、文件上传、搜索筛选再到后台管理和数据分析几乎把后端开发的常用技能点全占满了。尤其对于正在准备毕业设计、求职作品集或者想系统掌握Django全流程的人来说一个能跑通“浏览商品→加入购物车→提交订单→模拟支付→订单查询”闭环的商城项目远比零散刷教程有价值得多。我在技术选型时几乎没怎么犹豫就定下了Django。原因很直接它自带Admin后台、ORM、认证体系、表单处理、模板引擎和中间件机制这些在商城类项目里全都能直接派上用场。拿购物车来说如果不用Session而是自己写一套用户态存储光是并发加购时的数据一致性就够头疼一阵子而Django的Session框架加上ORM的事务控制能把这类基础问题降到最低。相比Flask的高度自由Django的“约定优于配置”虽然让部分追求灵活的开发者觉得受限但在商城这种业务链路长、模块边界清晰的项目里这种“框架替你做好决定”的特性反而能帮你把精力集中在业务核心上。再谈“大数据”这个关键词。很多人一听“商城大数据”就觉得要上Hadoop、Spark其实这是典型的本末倒置。对于教学型和毕设型的商城项目大数据的价值体现在两处一是数据的采集与清洗二是数据的分析与可视化展示。我采用的做法是在商城正常运行的基础上通过用户行为埋点记录访问日志然后对日志进行统计清洗最后用图表库做可视化看板模拟一个电商场景下的用户画像与商品热度分析闭环。这套流程虽然规模上和真正的企业级大数据平台差距很大但数据处理的思维链路是完整的用来展示和答辩完全够用。为了让项目的扩展性和维护性更好我在目录结构上分了四个Appusers用户模块、goods商品模块、orders订单模块和analytics数据分析模块每个App各自负责自己的URL路由、模型和视图。这么做的好处是后续无论是加一个秒杀功能还是接一个推荐算法都不会把代码搅成一团。整体项目我命名为django_mall数据库选用MySQL开发环境通过虚拟环境管理依赖后台管理系统直接复用Django自带的Admin并配合simpleui做界面美化这部分建议收藏后面章节我会把每一步的实操细节拆开讲。2. 环境准备与项目骨架搭建2.1 Python虚拟环境与依赖管理的正确姿势动工之前先把环境准备好。我本机用的是Python 3.10官方建议Django版本和Python版本要对得上Django 4.2 LTS是目前综合稳定性和新特性最合适的版本支持Python 3.8到3.12。这里建议不要直接用全局环境装Django而是用venv给项目单独建一个隔离环境否则以后项目一多依赖版本互相打架会让人很崩溃。# 创建项目目录并进入 mkdir django_mall cd django_mall # 创建虚拟环境 python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境macOS/Linux source venv/bin/activate # 安装Django、数据库驱动、以及其他依赖 pip install django4.2.* pip install mysqlclient pip install Pillow pip install simpleui pip install pandas pip install django-redis这里要解释几个安装项的必要性Pillow是图片验证码和商品缩略图上传的底层依赖没装它ImageField会直接报错django-redis用来做缓存我在项目里把商品详情页的渲染结果做了缓存从而让“大数据可视化分析”基于的日志统计不至于拖垮业务接口pandas是给后面的数据清洗和分析用的虽然商城项目数据量不大但pandas处理DataFrame确实比裸Python数组方便太多。虚拟环境激活后注意命令行前会出现(venv)前缀这说明环境生效了。如果用的是PyCharm直接在Settings里把Project Interpreter指向venv目录里的python.exe即可。很多新手在这里踩坑明明pip list能看到Django但运行python manage.py却提示找不到模块十有八九是解释器没有切到虚拟环境。2.2 创建项目与App模块划分环境就绪后开始创建项目骨架。这里强调一下App的拆分思路千万别图省事把所有逻辑写在一个App里。购物的核心链路包括会员体系、商品目录、交易流程这三者的业务边界非常清晰硬塞在一起后期改一个模块就可能影响另外两个。我在实际项目中按照“高内聚低耦合”的原则切分了模块django-admin startproject django_mall . python manage.py startapp users python manage.py startapp goods python manage.py startapp orders python manage.py startapp analytics创建完成后在django_mall/settings.py里把四个App注册进INSTALLED_APPS同时把simpleui放在Django自带admin之前否则主题不生效INSTALLED_APPS [ simpleui, django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, users, goods, orders, analytics, ]关于simpleui我再多提一句。Django默认的Admin界面是纯英文的简陋表格样式给答辩老师或团队展示时观感很一般。simpleui是一个开源的后台主题完全兼容Django原生Admin安装后在INSTALLED_APPS里把位置放在admin之前就能生效界面会变成带侧边栏和卡片布局的中文风格。最关键是它不需要改业务代码这对只想快速提升项目演示效果的人来说性价比极高。2.3 数据库配置与集成商城项目涉及强事务性操作订单扣库存、支付回调数据库选了MySQL。在settings.py里替换默认的sqlite配置DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: django_mall, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }这里有个关键细节字符集一定要用utf8mb4而不是utf8因为商品名称和用户昵称中可能出现emoji表情utf8格式存不下四个字节的字符。配置完成后在MySQL里执行CREATE DATABASE django_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;然后运行makemigrations和migrate验证数据库连通。我用表格总结一下环境搭建阶段的常见报错和解决方案都是实测过的报错信息原因解决方法ModuleNotFoundError: No module named MySQLdb缺少mysqlclientpip install mysqlclientWindows用户若编译失败可下载whl包安装django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb modulemysqlclient与Python版本不兼容换用pymysql并在__init__.py中执行pymysql.install_as_MySQLdb()AttributeError: str object has no attribute decodemysqlclient版本过旧升级mysqlclient到2.x版本中文乱码MySQL库或表字符集不是utf8mb4建库时明确指定utf8mb4连接OPTIONS也加上charset参数3. 核心业务模块设计与数据建模3.1 用户模块从Django自带User到自定义会员体系用户模块是商城的基础我直接扩展了Django自带的AbstractUser。为什么不直接建一张新表因为在Django的认证体系里request.user在视图和模板中会被大量使用如果另起炉灶自建用户表那登录态、权限校验、Admin关联全得自己重写得不偿失。扩展自带的User模型是最优雅的方案from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): mobile models.CharField(手机号, max_length11, uniqueTrue, nullTrue, blankTrue) avatar models.ImageField(头像, upload_toavatar/%Y/%m/, defaultavatar/default.png) balance models.DecimalField(余额, max_digits10, decimal_places2, default0) level models.CharField(会员等级, max_length20, default普通会员) created_at models.DateTimeField(auto_now_addTrue)注意要在settings.py里加上AUTH_USER_MODEL users.User并且一定要在第一次migrate之前配置好否则后期再换自定义用户模型会非常痛苦因为数据库里已经生成了auth_user表关联关系会乱掉。注册和登录我用的是Django内置的authenticate和login接口没有重复造轮子。密码字段Django自动做了哈希加密存储的是不可逆的密文这一点在答辩时经常被问到算是安全性的一个加分项。用户在登录后我通过一个简单的signal信号在User创建的同时自动创建对应的Profile扩展表用来存收货地址和积分这类更具体的业务数据。3.2 商品模块SPU和SKU的概念落地商品表的设计是整个商城数据模型的核心。初学者最常见的错误是把所有商品信息塞进一张表但真实电商场景里“商品”和“规格”是要分开的。我在项目中用SPUStandard Product Unit标准化产品单元和SKUStock Keeping Unit库存量单位两层结构来建模SPU代表一个商品比如“iPhone 15 Pro Max”描述的是商品的公共属性名称、品牌、详情图文、封面图。SKU代表具体可下单的规格组合比如“iPhone 15 Pro Max 256GB 原色钛金属”包含了价格、库存、SKU编码、规格JSON。这样设计的好处非常明显商品详情页展示的是SPU信息但用户选择规格后加到购物车的是SKU商品后台管理库存时只需要针对SKU表操作不会因为商品多规格导致数据冗余。代码实现如下class Category(models.Model): name models.CharField(分类名, max_length50) parent models.ForeignKey(self, on_deletemodels.CASCADE, nullTrue, blankTrue, verbose_name父分类) class Meta: verbose_name 商品分类 verbose_name_plural verbose_name def __str__(self): return self.name class SPU(models.Model): name models.CharField(商品名称, max_length200) subtitle models.CharField(副标题, max_length200, blankTrue) category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name分类) main_image models.ImageField(主图, upload_togoods/spu/%Y/%m/) detail models.TextField(商品详情HTML, blankTrue) sales models.IntegerField(销量, default0) is_on_sale models.BooleanField(是否上架, defaultTrue) created_at models.DateTimeField(auto_now_addTrue) class SKU(models.Model): spu models.ForeignKey(SPU, on_deletemodels.CASCADE, related_nameskus, verbose_name所属商品) specs models.JSONField(规格JSON, defaultdict) price models.DecimalField(价格, max_digits10, decimal_places2) stock models.IntegerField(库存, default0) code models.CharField(SKU编码, max_length50, uniqueTrue) image models.ImageField(规格图, upload_togoods/sku/%Y/%m/, nullTrue, blankTrue)分类表用自关联实现两级分类比如“手机数码”下有“手机通讯”和“摄影摄像”。on_deletemodels.PROTECT是为了防止分类下还有商品时被误删。JSONField规格字段可以直接存{颜色: 原色钛金属, 存储: 256GB}这类结构查询时用Django的JSONField查找语法过滤非常方便。3.3 订单模块事务与状态机的精妙配合订单模块是最能体现后端功力的环节。从购物车提交订单时至少涉及四张表的写操作生成订单主表、生成订单明细表、扣减SKU库存、清空购物车。任一步失败都可能导致数据不一致因此必须用事务把这几步包起来。Django的transaction.atomic装饰器是我推荐的首选方案from django.db import transaction from django.utils import timezone transaction.atomic def create_order(request, sku_id, quantity, address_id): sku SKU.objects.select_for_update().get(idsku_id) if sku.stock quantity: raise ValueError(库存不足) sku.stock - quantity sku.sales quantity sku.save() order Order.objects.create( userrequest.user, order_nogenerate_order_no(), total_amountsku.price * quantity, addressaddress_id, statuspending ) OrderItem.objects.create( orderorder, skusku, spu_namesku.spu.name, sku_specssku.specs, pricesku.price, quantityquantity ) cart_items CartItem.objects.filter(userrequest.user, skusku) cart_items.delete() return order这里代码里有几个细节值得反复揣摩。select_for_update()是行级锁它会在数据库层面锁定SKU这一行直到事务结束防止两个用户同时下单同一件商品时库存被超卖。这个机制非常经典面试时如果被问到“如何解决并发超卖”这就是标准答案。订单号我用了时间戳用户ID随机数的拼接方式虽然不复杂但在中小型项目里足够保证唯一性。订单状态我设计成字符串枚举pending待支付、paid已支付、shipped已发货、completed已完成、cancelled已取消。每一次状态流转都在视图里做合法性校验比如已发货的订单不允许直接取消。3.4 购物车Session临时态与数据库持久态并存购物车我采用了“未登录存Session登录后落库”的双轨策略。用户在未登录状态下也能浏览加购这是很多商城系统的标准交互——只有到最后结算时才强制登录。简单来说未登录用户的购物车数据存在服务端Session里登录后我通过一个合并逻辑把Session中的购物车条目和数据库中的CartItem合并def merge_cart(request): session_cart request.session.get(cart, {}) for sku_id, quantity in session_cart.items(): cart_item, created CartItem.objects.get_or_create( userrequest.user, sku_idsku_id, defaults{quantity: quantity} ) if not created: cart_item.quantity quantity cart_item.save() request.session[cart] {}这里要注意一个细节Session中存储的key是sku_idvalue是数量而不是sku_id和整个sku对象因为session是序列化存储的不能直接放对象实例。使用get_or_create的好处是无需先查询再判断一条语句就能完成“存在则更新、不存在则创建”的逻辑。4. 前端页面与核心交互实现4.1 商城首页渲染与模板继承体系前端部分我采用了Django模板原生JavaScriptBootstrap的组合没有引入复杂的Node.js构建链。选择模板渲染而不是前后端分离主要原因是在商城这种以内容展示为主的项目里服务端渲染对SEO更友好而且Django模板语法可以直接在HTML里写{% for item in goods_list %}对于个人开发者来说开发效率明显更高。布局上我做了一套基础模板base.html把导航栏、底部信息、CSS/JS的引入统一封装子页面只需要继承后覆写content块即可。导航栏的购物车角标是实时渲染的这里通过上下文处理器实现# context_processors.py def cart_count(request): if request.user.is_authenticated: count CartItem.objects.filter(userrequest.user).count() else: count sum(request.session.get(cart, {}).values()) return {cart_count: count}然后在settings.py的TEMPLATES配置里注册这个上下文处理器。这样所有页面模板中都能直接使用{{ cart_count }}无需每个视图重复把购物车数量传给模板省了大量重复代码也避免漏传导致模板变量为空。首页的推荐商品我使用了数据库查询的Q对象和F表达式做组合筛选比如把销量大于100或者评分高于4.5的商品优先展示然后在下方按分类瀑布流展示最新上架商品。首页一定要做缓存我用django-redis缓存了首页的商品推荐列表缓存时间为5分钟避免每次请求都打数据库。4.2 商品搜索与筛选ORM动态条件拼接搜索功能是商城系统的标配但怎么写得好是有讲究的。Django的ORM查询是一个懒加载QuerySet可以在不访问数据库的情况下不断拼接过滤条件非常适合动态筛选场景def product_list(request): goods SPU.objects.filter(is_on_saleTrue).select_related(category).prefetch_related(skus) keyword request.GET.get(q, ) category_id request.GET.get(category, ) price_min request.GET.get(price_min, ) price_max request.GET.get(price_max, ) order_by request.GET.get(sort, default) if keyword: goods goods.filter(Q(name__icontainskeyword) | Q(subtitle__icontainskeyword)) if category_id: goods goods.filter(category_idcategory_id) if price_min: goods goods.filter(skus__price__gteprice_min) if price_max: goods goods.filter(skus__price__lteprice_max) if order_by sales: goods goods.order_by(-sales) elif order_by price_asc: goods goods.order_by(skus__price) elif order_by price_desc: goods goods.order_by(-skus__price)这个视图的逻辑接近实际电商平台的搜索原型。Q对象实现标题和副标题的模糊匹配skus__price的跨表过滤会自动生成JOIN查询。价格排序那里我用了order_by(skus__price)需要注意如果同一个SPU下有多个SKU可能会产生重复记录某些场景下还需要用distinct()去重。异步加载方面我使用了AjaxJSON接口实现分类筛选时局部刷新商品列表通过Django的JsonResponse返回数据前端用fetch抓取后重新渲染商品卡片区域。这么做虽然不完全算SPA但比整页刷新体验好很多。接口返回序列化数据时我直接调用模型对象的值拼成dict返回没有用Django REST Framework的Serializer因为简单场景没有必要引入额外依赖。4.3 商品详情与图片上传功能商品详情页是另一个重点。页面布局是左侧大图规格选择卡片、右侧价格和加购按钮。规格选择时前端JavaScript监听规格选项的点击事件将选中的规格组合发送到后端接口查询对应的SKUasync function getSkuBySpecs(spuId, specs) { const params new URLSearchParams(); params.append(spu_id, spuId); params.append(specs, JSON.stringify(specs)); const resp await fetch(/api/get_sku/, { method: POST, body: params }); const data await resp.json(); if (data.code 200) { document.getElementById(sku_price).innerText ¥ data.sku.price; document.getElementById(sku_stock).innerText 库存 data.sku.stock; // 更新加入购物车按钮的自定义属性 document.getElementById(add_to_cart_btn).dataset.skuId data.sku.id; } }图片上传方面最常用的是商品后台管理图片上传但也需要在用户端实现用户头像上传。用户头像上传我单独写了一处视图使用表单提交校验文件类型和后缀用ContentType判断MIME类型禁止上传exe、php等可执行文件。文件保存到media目录后把文件路径更新到User.avatar字段。这里有几个安全细节图片文件重命名为UUID名称防止重名覆盖限制文件大小不超过2MB否则直接拒绝并提示用户。4.4 订单提交与模拟支付流程支付功能是商城闭环里最容易把初学者卡住的一环。现实环境里想接入支付宝或微信支付需要企业资质和备案域名个人开发者没有条件。我的做法是做一个模拟支付页面用户确认订单后跳转到支付页页面展示订单金额和一个“立即支付”按钮点击后模拟支付成功并回调更新订单状态。支付成功后后端执行的逻辑def pay_callback(request, order_no): order Order.objects.select_for_update().get(order_noorder_no) if order.status ! pending: return JsonResponse({code: 400, msg: 订单状态异常}) order.status paid order.paid_time timezone.now() order.save() # 清理Redis中该用户的相关缓存 cache.delete(forder_list_{request.user.id}) return JsonResponse({code: 200, msg: 支付成功})模拟支付的流程完整走通了“待支付→已支付”的状态流转后面如果想接入真实支付网关只需要把支付发起页的跳转URL替换为支付宝的授权链接并在回调接口中验签后调用同样的order状态更新逻辑即可。那种“看代码感觉很懂自己一写就报错”的问题往往就出在订单状态机的校验和事务边界这些细节上没有想清楚。5. 大数据分析模块从埋点到可视化5.1 用户行为埋点与日志采集商城跑起来后我加了一个“大数据分析”模块用来回答三个问题哪些商品最受欢迎用户逛商城的时段分布是怎样的不同分类的商品贡献了多少销售额这些问题的答案来自埋点数据也就是用户在前端页面上的操作行为日志。埋点我做的比较轻量前端在商品详情页加载完成后通过一个异步请求上报用户浏览记录window.onload function() { fetch(/api/track/, { method: POST, headers: {X-CSRFToken: getCookie(csrftoken)}, body: JSON.stringify({ event_type: product_view, spu_id: {{ spu.id }}, stay_seconds: 0, page: location.pathname }) }); };后端收到埋点数据后写入一张独立的事件日志表。这张表只做写入不参与业务查询避免大数据量的日志影响商城核心接口性能。日志表中我会记录事件ID、用户ID匿名用户记录为null、事件类型、目标商品ID、页面路径、访问时间、UA设备信息。字段统一用整型和时间类型存储方便后续统计聚合。5.2 基于pandas的数据清洗与统计原始埋点数据是不能直接用来做可视化的里面有大量噪声爬虫流量、用户频繁刷新产生的重复点击、超短停留时间的误触行为都要在统计之前清洗掉。我写了一个管理命令management command定时执行清洗和统计任务输出聚合结果到分析表from django.core.management.base import BaseCommand import pandas as pd from django.utils.timezone import now class Command(BaseCommand): help 析埋点数据并生成统计结果 def handle(self, *args, **options): events list(EventLog.objects.filter(created_at__datenow().date()).values( id, user_id, event_type, spu_id, created_at, ua) ) if not events: return df pd.DataFrame(events) # 过滤无商品ID的无效事件 df df[df[spu_id].notnull()] # 按小时聚合浏览PV df[hour] df[created_at].apply(lambda x: x.hour) pv_hour df.groupby(hour).size().reset_index(namepv) # 统计每个商品的热度值浏览量2*加购次数 df[weight] df[event_type].map({product_view: 1, add_to_cart: 2}).fillna(0.5) product_hot df.groupby(spu_id)[weight].sum().reset_index(namehot_score) # 入库供展示接口读取 ...这里体现了一个核心思路pandas做二维表格数据的聚合计算比Django的ORM聚合更灵活尤其是多维度交叉分析场景。而Django的ORM擅长的是对象化的增删改查两者搭配使用效率非常高。5.3 可视化看板PV/UV统计与商品热度排行数据统计出来以后我用ECharts做可视化看板。看板页面包含三个核心图表折线图最近7天每天的PV/UV趋势直观展示商城流量变化。柱状图商品热度Top10可以直接看到哪些商品是最受欢迎的主推款。饼图商品分类销售占比分析商城商品结构是否健康。后端通过JSON接口把聚合好的数据传给前端前端用ECharts初始化图表fetch(/api/analytics/summary/) .then(resp resp.json()) .then(data { const pvChart echarts.init(document.getElementById(pv_chart)); pvChart.setOption({ xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [ { name: PV, type: line, data: data.pv_list, smooth: true }, { name: UV, type: line, data: data.uv_list, smooth: true } ] }); });这个看板放在管理后台的子菜单中同时也做了一个只读页面供普通访客查看。答辩或者给朋友演示的时候先逛几个商品页面然后打开看板看到柱状图里对应的商品热度上升效果非常直观比空口说“这个系统能分析大数据”有说服力得多。实际上这个分析模块就是一个简化版的数据仓库ETL流程采集埋点→清洗过滤→建模聚合统计→应用可视化展示。规模虽小五脏俱全。如果后续要继续深造大数据技术栈完全可以把这个模块替换成FlumeKafka做日志采集Spark/Flink做流式统计ClickHouse做OLAP存储思路是完全相通的。6. 常见问题与排查技巧实录6.1 数据库与迁移那些坑整个开发过程中我遇到最多的问题都集中在数据库和迁移上。最典型的一个是执行makemigrations时报“TypeError:init() missing 1 required positional argument: on_delete”。这个原因是Django 2.0以后ForeignKey、OneToOneField必须显式指定on_delete行为以前可以省略。解决方法是在所有外键字段后加on_deletemodels.CASCADE或者按业务需要改成PROTECT、SET_NULL。还有一个高频报错是migrate时提示“Table django_mall.auth_user doesnt exist”。这个情况多半是自定义了User模型但AUTH_USER_MODEL没有在第一次migrate前配置导致Django按默认模型生成了迁移记录。解决办法最干净的是如果项目刚开始直接把数据库drop掉重新配置好AUTH_USER_MODEL后再makemigrations和migrate。如果已经线上跑了一段时间就得用数据迁移脚本把旧数据导入新表操作成本高很多所以一开始就确认好自定义用户模型很重要。6.2 静态文件与媒体文件的部署问题开发环境下静态文件和媒体文件由Django自带服务器托管一部署到真实服务器就会出现CSS样式丢失、图片打不开的情况。核心原因是DEBUGFalse后Django不再自动处理静态文件。解决方法是在settings.py里配置STATIC_URL /static/ STATIC_ROOT os.path.join(BASE_DIR, staticfiles) MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)然后在项目根URL里追加from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)部署到Nginx后由Nginx直接服务/media/和/static/目录Django只处理动态请求。很多初学者开发时图片一切正常一部署就看不到图片十有八九就是没有配置Media的URL映射。6.3 购物车和订单的常见边界场景购物车最常见的bug是“不同用户看到彼此购物车数据”。这个问题的根源往往是Session配置问题在settings.py里如果没有设置SESSION_COOKIE_HTTPONLY或者Cookie的path设置过宽就可能导致会话串号。排查方法很简单用浏览器的开发者工具查看Cookie中sessionid的值是否在不同浏览器登录时保持一致如果一致说明Session没有正确隔离。订单模块的边界场景更多用户重复提交订单、支付回调超时重试、库存刚好为0时并发下单。这三类问题我在代码里都做了防御重复提交订单前端提交设置为点击后禁用按钮后端用Redis SETNX命令做幂等标记同一用户同一订单号5秒内不允许重复提交。回调重试在支付回调视图里先判断订单状态如果不是pending则直接返回成功应答不重复执行扣减逻辑。零库存并发下单select_for_update行级锁保证同一SKU的并发操作串行化锁等待超时则返回友好提示。下面是一个综合速查表列出我遇到的典型问题和处理方式问题现象根因分析排查命令/方法解决方案登录后刷新页面又变未登录Session过期问题中SESSION_SAVE_EVERY_REQUEST未开启查看浏览器Cookie是否持续写入设置SESSION_SAVE_EVERY_REQUESTTrue并检查SESSION_COOKIE_AGE商品搜索速度慢所有字段都用了icontains模糊查询EXPLAIN查看SQL执行计划对name字段添加全文索引或引入Elasticsearch做搜索层上传大图片超时Nginx默认请求体大小限制1MB查看Nginx error.log修改client_max_body_size 20m订单状态变为已支付但库存未扣支付回调没走事务检查代码是否在transaction.atomic内将库存扣减和订单更新包进同一事务后台商品图片不显示忘记配MEDIA_URL映射访问http://域名/media/商品图路径在URLconf中添加static()映射7. 项目部署与后续扩展建议把开发好的商城系统部署到真实服务器是很多人的最后一个坎。我的操作流程是服务器使用宝塔面板统一管理Nginx和MySQLPython环境用宝塔的Python项目管理器创建然后把本地的venv依赖导出成一个requirements.txt上传到服务器在线上环境中pip install -r requirements.txt。Python项目管理器最重要的是设置启动命令和端口。我的启动命令是gunicorn django_mall.wsgi:application --bind 0.0.0.0:8000 --workers 3gunicorn是Python常用的生产级WSGI服务器比Django自带的runserver稳定得多。workers数量一般设置为CPU核心数的2倍加1我的测试机是2核4G所以配3个worker。配好之后在Nginx配置一个反向代理把80端口的流量转发到8000端口server { listen 80; server_name your_domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /www/wwwroot/django_mall/staticfiles/; } location /media/ { alias /www/wwwroot/django_mall/media/; } }这个配置部署完毕后别忘了执行python manage.py collectstatic把Django的静态文件收集到STATIC_ROOT指向的目录。这个命令会把Admin的CSS、JS文件也一并收集如果不执行后台管理页面的样式会全部丢失。项目做完后后续扩展方向其实非常多。我列几个我认为增值空间最大的方向接入真实微信支付或支付宝沙箱支付、引入Redis队列做秒杀功能、用Celery做订单超时自动取消、把搜索模块升级为Elasticsearch、在数据分析模块中加入用户画像和个性化推荐算法。尤其是秒杀和推荐这两个方向做好了能让项目从“功能完整”提升到“有亮点可讲”。另外如果你打算把这个项目作为毕设作品我强烈建议写一份详细的设计文档重点说明技术选型理由和数据库设计思路。答辩时老师通常不会只问代码怎么实现更多的是问“为什么这么设计”和“如果数据量大了怎么办”这两个问题在本文第2章和第5章都有对应的思考路径可以参考。我在实际操练这个项目时最大的感受是一个完整的商城系统真正难的不是某个单独的知识点而是如何把各个模块串成一个整体。订单和库存的一致性、Session和数据库的切换、日志采集和分析展示的联动这些跨模块的交互才是一个项目真正有价值的地方。希望这篇实战记录能帮你把Django购物商城从理论变成本地能跑通的代码少走一些我走过的弯路。