简介这是一套基于Django框架的Python全栈项目源码以手机商城为主题适合正在学习Web全栈开发、需要完整项目练手或课程设计参考的开发者。项目采用前后端分离思路前端为微信小程序主要负责商品展示与页面交互后端依托Django搭建管理后台完成数据维护与接口支撑能帮助读者理解从数据建模到接口联调的完整链路。资源包共102个文件约497KB包含16个py后端逻辑文件、13个js脚本、13个json配置、11个wxss与9个wxml小程序页面文件以及若干png、jpg图片素材和4个html模板另附sqlite数据库与说明文档结构紧凑、便于快速运行与二次修改。目前已有154人学习下载。通过研读这套源码读者可掌握Django项目目录组织、模型与视图编写、小程序页面渲染及前后端数据对接等关键技能也可作为毕业设计或课程作业的参考方案按模块拆解后逐层理解业务实现。1. 从零手搓手机商城这套 Django 全栈源码到底能跑通什么如果你正在找一个能写进简历、能跑通支付流程、还能把 Django 全栈链路完整串起来的练手项目这套手机商城源码值得花时间拆一遍。它不是那种只做了增删改查的玩具——商品 SKU、购物车合并、订单状态机、后台管理、用户鉴权这几块都落到了代码里。我见过太多人学完 Django 教程后卡在“不知道下一步做什么”这个项目的价值就在于它把 MTV 模式、ORM 关联查询、Session 与 Cookie 配合、模板继承这些知识点塞进了一个有真实业务闭环的场景里。适合谁刚学完 Python 基础、想用 Django 做第一个完整项目的人或者已经会写视图但没处理过订单并发、库存扣减这类问题的开发者。源码包结构清晰按 app 拆分拿到手先跑起来再逐模块读比对着文档空想效率高得多。2. 环境搭建与项目初始化把源码跑起来的第一道坎2.1 依赖版本与虚拟环境隔离这套项目基于 Django 框架但 Django 版本差异会直接影响 ORM 查询语法和urls.py的路由写法。我一般会先看requirements.txt里锁定的版本没有的话就按python -c import django; print(django.get_version())反查。常见做法是用venv建独立环境避免和系统 Python 里的包打架。# 创建虚拟环境指定 Python 3.8 以上 python -m venv venv # 激活环境Windows 用 venv\Scripts\activate source venv/bin/activate # 安装依赖建议逐条装而不是一把梭 pip install django3.2.18 pip install pillow # 商品图片上传依赖 pip install mysqlclient # 如果数据库用 MySQL这里有个参数要留意mysqlclient在 Windows 上编译经常翻车报Microsoft Visual C 14.0 is required。解决方式要么装 Build Tools要么换pymysql并在__init__.py里加pymysql.install_as_MySQLdb()。我一般会先确认数据库选型——源码默认可能是 SQLite但手机商城这种有订单和库存的场景SQLite 的写锁在并发下会拖后腿建议切到 MySQL 或 PostgreSQL。2.2 数据库配置与迁移命令打开settings.py找到DATABASES配置段。如果是 SQLiteENGINE是django.db.backends.sqlite3NAME指向BASE_DIR / db.sqlite3。切 MySQL 的话按下面改# settings.py 数据库配置 DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: phone_mall, # 提前在 MySQL 里建好库 USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, # 支持 emoji 和生僻字 }, } }改完执行迁移。这里注意如果源码里带了migrations文件夹直接migrate就行如果没有先makemigrations再migrate。顺序反了会报Table django_migrations doesnt exist。python manage.py makemigrations python manage.py migrate # 创建后台管理员按提示输入用户名密码 python manage.py createsuperuser # 启动开发服务器默认 8000 端口 python manage.py runserver启动后访问127.0.0.1:8000/admin能进后台说明数据库和鉴权模块通了。如果报TemplateDoesNotExist检查TEMPLATES里的DIRS是否指向了项目的templates目录——这是新手最常踩的坑源码里模板路径写的是相对路径换目录后必须同步改。2.3 静态文件与媒体文件路径手机商城离不开商品图。STATIC_URL和MEDIA_URL要分开配MEDIA_ROOT指向用户上传目录STATICFILES_DIRS指向开发者放的 CSS/JS。开发阶段用django.conf.urls.static提供媒体文件访问上线后必须交给 Nginx否则 Django 处理静态文件会拖垮性能。# urls.py 末尾追加仅开发环境用 from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)提示DEBUG True时 Django 才会自动处理静态文件上线前改成False并配ALLOWED_HOSTS否则页面样式全丢。3. 商品与购物车模块ORM 关联查询和 Session 合并逻辑3.1 商品模型设计与 SKU 拆分手机商城和普通商城最大的区别在 SKU 维度——同一款手机有颜色、内存、版本三个属性组合。源码里常见做法是Category、Goods、GoodsSKU三层模型用外键串起来。# models.py 商品模型核心字段 class Category(models.Model): name models.CharField(max_length50) parent models.ForeignKey(self, nullTrue, blankTrue, on_deletemodels.CASCADE) class Goods(models.Model): name models.CharField(max_length100) category models.ForeignKey(Category, on_deletemodels.CASCADE) price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0) image models.ImageField(upload_togoods/%Y/%m/) is_on_sale models.BooleanField(defaultTrue) # 上下架开关 class GoodsSKU(models.Model): goods models.ForeignKey(Goods, on_deletemodels.CASCADE) color models.CharField(max_length20) version models.CharField(max_length20) price models.DecimalField(max_digits10, decimal_places2)DecimalField而不是FloatField是血泪经验——浮点数算钱会出现0.1 0.2 0.30000000000000004这种玄学结果订单金额对不上账。upload_to带日期路径是为了避免单目录文件过多导致ls卡死。查询时用select_related减少数据库往返# 查商品详情时一次性把分类和 SKU 拉出来 goods Goods.objects.select_related(category).prefetch_related(goodssku_set).get(idgoods_id)select_related走 SQL JOIN适合外键prefetch_related走两次查询再在 Python 里拼适合多对多和反向外键。用反了不会报错但查询次数会从 2 次涨到 N1 次。3.2 购物车 Session 存储与登录合并未登录用户的购物车存 Session登录后要合并到数据库。源码里一般用cart_key做标识结构是{sku_id: count}。# views.py 购物车添加逻辑 def add_to_cart(request): sku_id request.POST.get(sku_id) count int(request.POST.get(count, 1)) cart request.session.get(cart, {}) # 已存在则累加不存在则初始化 cart[sku_id] cart.get(sku_id, 0) count request.session[cart] cart return JsonResponse({code: 0, cart_count: len(cart)})登录合并时遍历 Session 里的cart逐条写入Cart模型已存在的记录做数量累加而不是覆盖。这里有个坑Session 默认序列化用 JSONkey 必须是字符串sku_id是 int 的话取出来会变 str比较时要统一类型否则cart.get(sku_id)永远返回None。3.3 库存扣减与并发处理下单时扣库存不能先查再改两个请求同时进来会超卖。正确做法是用F表达式做原子更新from django.db.models import F # 原子扣减stock 小于 count 时更新 0 行 updated GoodsSKU.objects.filter(idsku_id, stock__gtecount).update(stockF(stock) - count) if not updated: return JsonResponse({code: 1, msg: 库存不足})filter里的stock__gtecount是条件锁update返回受影响行数为 0 说明库存不够。这比select_for_update轻量适合中小规模并发。如果项目要扛更高并发得引入 Redis 做库存预扣但那是另一个量级的改造了。4. 订单与支付链路状态机、超时取消与回调验签4.1 订单状态流转设计订单不是一张表存到底状态字段要能表达完整生命周期。源码里常见状态值待支付、已支付、待发货、已发货、已完成、已取消。用choices约束取值范围避免脏数据。class Order(models.Model): STATUS_CHOICES ( (1, 待支付), (2, 已支付), (3, 待发货), (4, 已发货), (5, 已完成), (6, 已取消), ) status models.SmallIntegerField(choicesSTATUS_CHOICES, default1) order_sn models.CharField(max_length30, uniqueTrue) # 订单号 total_amount models.DecimalField(max_digits10, decimal_places2) create_time models.DateTimeField(auto_now_addTrue)状态流转必须单向不能从「已完成」跳回「待支付」。我一般会在视图层加判断if order.status ! 1: return error只允许待支付订单发起支付。4.2 订单号生成与超时取消订单号用「时间戳 用户 ID 后四位 随机数」拼保证唯一且可读。超时取消用 Celery 延时任务或定时脚本扫create_time超过 30 分钟且状态为待支付的订单。import time, random def generate_order_sn(user_id): # 格式20250101120000 用户后四位 6位随机 timestamp time.strftime(%Y%m%d%H%M%S) rand str(random.randint(100000, 999999)) return f{timestamp}{str(user_id)[-4:]}{rand}超时取消的脚本逻辑from datetime import datetime, timedelta def cancel_expired_orders(): deadline datetime.now() - timedelta(minutes30) expired Order.objects.filter(status1, create_time__ltdeadline) for order in expired: order.status 6 order.save() # 回滚库存用 F 表达式加回去 for item in order.orderitem_set.all(): GoodsSKU.objects.filter(iditem.sku_id).update(stockF(stock) item.count)注意回滚库存和改状态要放在同一个事务里否则中途报错会出现「订单取消了但库存没回来」的黑匣子情况。4.3 支付回调验签与幂等处理支付平台回调时先验签再改状态。验签用平台提供的公钥和签名算法源码里一般封装在utils/pay.py。幂等处理是关键——同一笔回调可能重复到达必须判断订单当前状态已支付就直接返回成功不重复发货。def pay_callback(request): data request.POST.dict() if not verify_sign(data): # 验签失败直接拒绝 return HttpResponse(fail) order_sn data.get(out_trade_no) order Order.objects.get(order_snorder_sn) if order.status ! 1: # 幂等非待支付状态直接返回 return HttpResponse(success) order.status 2 order.save() return HttpResponse(success)注意回调地址必须是公网可访问的本地开发用内网穿透工具临时映射别把127.0.0.1填进去否则永远收不到回调。5. 避坑与排查那些让项目跑不起来的常见问题5.1 迁移文件冲突导致 migrate 失败现象执行python manage.py migrate报InconsistentMigrationHistory或Table already exists。原因手动改过模型但没重新生成迁移或者多人协作时迁移文件版本对不上。解决删掉对应 app 下migrations里除__init__.py外的文件删掉数据库里的django_migrations表重新makemigrationsmigrate。数据重要的话先备份。5.2 模板中静态文件路径 404现象页面能打开但 CSS/JS 全丢控制台报 404。原因模板里写的是{% static css/style.css %}但STATICFILES_DIRS没配或路径不对。解决确认settings.py里STATICFILES_DIRS [BASE_DIR / static]且static目录和manage.py同级。DEBUGFalse时还要配 Nginx 的location /static/。5.3 图片上传后无法访问现象后台上传商品图成功前端img标签裂开。原因MEDIA_URL配了但urls.py没加static()路由或者MEDIA_ROOT目录没有写权限。解决开发环境在urls.py追加static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)Linux 下chmod 755媒体目录。5.4 购物车数量显示为字符串拼接现象购物车角标显示11而不是2。原因Session 里存的 count 是字符串变成了拼接。解决存之前int(count)强转取出来也转一次。Django Session 序列化不会保留 int 类型这是设计如此。5.5 订单提交后库存没变现象下单成功但商品库存还是原值。原因扣库存代码写在order.save()之后且没加事务或者用了Goods而不是GoodsSKU扣减。解决用transaction.atomic()包住下单逻辑确认扣的是 SKU 表的 stock 字段。6. 进阶技巧用 Django Debug Toolbar 定位 N1 查询与缓存优化项目跑通之后真正拉开差距的是性能排查。我习惯在settings.py里挂上 Django Debug Toolbar它能把每个请求的 SQL 条数、耗时、重复查询全列出来。安装很简单pip install django-debug-toolbar# settings.py INSTALLED_APPS [debug_toolbar] MIDDLEWARE.insert(0, debug_toolbar.middleware.DebugToolbarMiddleware) INTERNAL_IPS [127.0.0.1] # 只有本机访问才显示面板# urls.py 开发环境追加 if settings.DEBUG: import debug_toolbar urlpatterns [path(__debug__/, include(debug_toolbar.urls))]刷新商品列表页看 SQL 面板。如果发现同一个SELECT执行了 20 次那就是 N1 问题。比如循环里取sku.goods.name每次循环都查一次Goods表。改成prefetch_related(goodssku_set__goods)后 SQL 条数会从 21 降到 2。另一个高频优化点是首页商品列表。手机商城首页通常要展示分类、热销、新品三块数据如果每块都单独查库一个请求打十几次 SQL。我一般用 Django 的cache框架把结果缓存 60 秒from django.core.cache import cache def index(request): goods_list cache.get(index_goods) if not goods_list: goods_list list(Goods.objects.filter(is_on_saleTrue)[:10]) cache.set(index_goods, goods_list, 60) # 缓存 60 秒 return render(request, index.html, {goods_list: goods_list})缓存时间别设太长商品上下架后要等缓存过期才生效。如果对实时性要求高就在后台改商品时主动cache.delete(index_goods)。还有个小技巧DEBUGFalse后静态文件交给 Nginx但 Django 的runserver不再处理媒体文件本地测试支付回调时图片会 404。我一般会在开发阶段用whitenoise中间件顶一下上线再摘掉。从那以后我每次拿到新源码都强制先跑python manage.py check --deploy过一遍安全检查再挂 Debug Toolbar 看首页 SQL 条数最后才读业务代码。这个顺序能省掉大量「跑不起来」的无效折腾。希望帮到你。本文还有配套的精品资源点击获取