简介这是一套基于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 依然是快速交付的稳妥选择做过几个电商类外包的同行大概都有体会客户要的从来不是「最先进的技术栈」而是「下个月能不能上线、后台能不能改价、订单别丢」。手机商城这类项目尤其典型SKU 有颜色和容量两个维度、下单要锁库存、支付要回调对账、后台运营要能自己改 banner。这些需求拆开看都不难难的是把它们在两周内拼成一个能跑、能交付、能维护的整体。Django 在这个场景里的价值不是它有多快而是它把用户体系、Admin 后台、ORM、表单校验、CSRF 防护这些电商必备件全部内置了你少写的每一行胶水代码都是后期少踩的一个坑。这篇笔记就按「手机商城」这个具体品类把从建表到下单落库的完整链路讲一遍适合能写 Python、想接一个完整全栈项目练手或交付的开发者。2. 数据建模手机商城的 SKU 为什么不能只有一张商品表手机商城和服装、食品最大的区别在于规格组合是结构化的一台机器固定有颜色和存储容量两个销售属性组合出来的就是 SKU。很多新手第一版会直接在商品表上加color和storage两个字段结果同一款手机要插好几行改一次价格要 update 多条记录库存还对不上。正确做法是把「商品」和「SKU」拆成两层商品层放公共信息品牌、型号、详情图SKU 层放可售单元颜色容量价格库存。2.1 商品、SKU、分类三张核心表的字段设计先看模型定义这里用 Django 的 ORM 写注意related_name和索引的设置后面查列表页会用到。# shop/models.py from django.db import models class Category(models.Model): name models.CharField(max_length50, uniqueTrue) sort models.IntegerField(default0) # 后台排序用越小越靠前 class Meta: ordering [sort] class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, related_nameproducts) name models.CharField(max_length120, db_indexTrue) brand models.CharField(max_length50) cover models.ImageField(upload_toproduct/cover/) detail models.TextField(blankTrue) is_on_sale models.BooleanField(defaultTrue) # 下架不删数据 created_at models.DateTimeField(auto_now_addTrue) class Meta: indexes [models.Index(fields[category, is_on_sale])] class SKU(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, related_nameskus) color models.CharField(max_length20) storage models.CharField(max_length20) # 用字符串存 128GB避免单位混乱 price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0) sku_code models.CharField(max_length40, uniqueTrue) # 对接仓储用 class Meta: unique_together (product, color, storage) # 防重复组合逻辑说明Category用PROTECT而不是CASCADE是因为分类被误删会连带清空商品运营事故里这类问题排前三。SKU上的unique_together是硬约束防止后台运营重复录入「黑色 128G」。sku_code单独留一个字段是为了将来对接第三方仓储或 ERP 时不用改主键。参数说明price必须用DecimalField用FloatField会在满减、优惠券叠加时出现0.10.2这类精度问题对账时非常难查。stock用IntegerField而不是PositiveIntegerField是因为部分场景需要允许负库存占位比如预售具体看业务但字段类型先留余地。2.2 迁移命令与后台注册的最小闭环模型写完先跑迁移再注册到 Admin这样运营当天就能录数据。python manage.py makemigrations shop python manage.py migrate python manage.py createsuperuser# shop/admin.py from django.contrib import admin from .models import Category, Product, SKU class SKUInline(admin.TabularInline): # 在商品页直接编辑 SKU model SKU extra 1 admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display (name, brand, category, is_on_sale) list_filter (category, is_on_sale) inlines [SKUInline] admin.site.register(Category)逻辑说明TabularInline让运营在编辑商品时直接增删 SKU不用来回跳页面这是手机商城后台使用频率最高的操作。list_filter加上is_on_sale是为了让运营快速筛出下架商品做批量处理。参数说明extra 1表示默认显示一行空 SKU 表单设太大页面会卡设 0 则运营找不到新增入口1 是实测比较顺手的值。3. 购物车与下单库存扣减到底该在哪一步做购物车和下单是手机商城最容易出 bug 的两块。购物车可以放 Session也可以放数据库取决于要不要跨设备同步下单则必须解决「两个人同时买最后一台」的超卖问题。这一章把两条链路都走一遍。3.1 购物车用 Session 还是数据库看这三个判断点判断标准很直接要不要登录后才能加购、要不要跨设备同步、要不要做弃购召回。手机商城通常三个都要所以用数据库表更稳。# cart/models.py from django.db import models from django.contrib.auth.models import User from shop.models import SKU class CartItem(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namecart_items) sku models.ForeignKey(SKU, on_deletemodels.CASCADE) quantity models.IntegerField(default1) added_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, sku) # 同一 SKU 只累加数量逻辑说明unique_together保证同一用户对同一 SKU 只有一条记录加购时用update_or_create累加数量避免购物车出现两行相同 SKU。参数说明quantity在加购接口里要做上限校验常见做法是单 SKU 不超过 200防止有人写脚本刷库存占用。3.2 下单接口事务、行锁与库存扣减的先后顺序下单的核心是把「校验库存 → 扣库存 → 生成订单 → 清购物车」放进一个事务并且对 SKU 行加锁。# order/views.py from django.db import transaction from django.shortcuts import get_object_or_404 from rest_framework.decorators import api_view from rest_framework.response import Response from shop.models import SKU from .models import Order, OrderItem api_view([POST]) def create_order(request): sku_id request.data.get(sku_id) qty int(request.data.get(quantity, 1)) with transaction.atomic(): # select_for_update 对 SKU 行加锁阻塞并发下单 sku SKU.objects.select_for_update().get(idsku_id) if sku.stock qty: return Response({code: 4001, msg: 库存不足}, status400) sku.stock - qty sku.save(update_fields[stock]) order Order.objects.create(userrequest.user, totalsku.price * qty) OrderItem.objects.create(orderorder, skusku, pricesku.price, quantityqty) return Response({code: 0, order_id: order.id})逻辑说明select_for_update()必须在事务内使用它会对查询到的 SKU 行加排他锁第二个并发请求会阻塞到第一个事务提交从而避免超卖。注意sku.save(update_fields[stock])只更新库存字段减少写放大。参数说明transaction.atomic()的粒度要控制好不要把发短信、写日志这类外部调用放进去否则事务持有时间过长锁等待会拖垮下单接口。库存判断用而不是是因为stock qty时应该允许下单。提示如果用的是 MySQLselect_for_update需要 InnoDB 引擎且关闭自动提交SQLite 在开发环境会退化成整库锁压测数据不可信上线前务必在目标数据库上验证。4. 避坑与排查手机商城项目里最容易翻车的五个点这一章全是血泪经验每条都按「现象 → 原因 → 解决」写遇到对应症状直接对号入座。4.1 图片上传后前台 404现象Admin 后台上传了商品封面前台img标签打开是裂图控制台报 404。原因开发环境没有配置MEDIA_URL和MEDIA_ROOT的路由Django 默认不服务媒体文件。解决在settings.py里加MEDIA_URL /media/和MEDIA_ROOT BASE_DIR / media并在urls.py里追加static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)。注意这段只在DEBUGTrue时生效生产环境要交给 Nginx 或对象存储。4.2 下单接口偶发超卖现象压测时库存 10 件卖出 12 单。原因库存判断和扣减之间没有加锁两个请求同时读到stock1都判断通过。解决按 3.2 的方式用select_for_update包住或者用数据库层面的UPDATE ... SET stock stock - 1 WHERE stock 1原子更新判断受影响行数是否为 1。4.3 价格字段出现 0.30000000000000004现象订单金额算出来带一长串小数。原因用了FloatField或 Python 原生float做金额运算。解决模型层统一DecimalField运算时用Decimal对象序列化输出时用str()或 DRF 的DecimalField不要中途转 float。4.4 后台删除分类导致商品全部消失现象运营删了一个分类该分类下商品全部查不到。原因外键用了CASCADE。解决改成PROTECT删除时抛ProtectedError让运营先处理商品再删分类。如果业务确实要删做软删除加is_deleted字段。4.5 列表页 N1 查询拖慢接口现象商品列表页每行都要查一次分类名和最低价20 条数据发了 40 多条 SQL。原因模板或序列化器里直接访问product.category.name和product.skus.first()。解决查询时用select_related(category)和prefetch_related(skus)把关联数据一次拉回来。用 Django Debug Toolbar 看 SQL 条数超过 5 条就要警惕。5. 从能跑到能交付三个让手机商城项目更值钱的进阶技巧项目能跑起来只是及格线能不能交付、能不能被接手看的是后面这些细节。第一个技巧是给订单状态机加约束。很多新手用status字段存 0/1/2但没有任何校验结果出现「已取消」的订单又被改成「已发货」。常见做法是定义一个状态流转字典在save()或 service 层校验。# order/models.py ORDER_FLOW { pending: [paid, canceled], paid: [shipped, refunded], shipped: [done], done: [], canceled: [], } class Order(models.Model): STATUS_CHOICES [(k, k) for k in ORDER_FLOW] status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpending) def can_transit(self, target): return target in ORDER_FLOW.get(self.status, [])逻辑说明ORDER_FLOW把合法流转集中在一处改业务规则只动这一个字典。can_transit在视图层调用不合法就返回 400避免脏状态写库。第二个技巧是给 SKU 价格加历史表。手机降价频繁订单里必须记录「下单时的价格」而不是关联 SKU 现价。OrderItem里冗余一个price字段就是干这个的别图省事用sku.price反查否则改价后历史订单金额全变。第三个技巧是用manage.py check --deploy做上线前自检。这条命令会检查DEBUG、SECRET_KEY、ALLOWED_HOSTS、Cookie 安全属性等十几项配置输出里带WARNINGS的逐条处理。我一般会在 CI 里加一步配置有问题直接卡住构建比上线后被打回强。检查项开发环境生产环境DEBUGTrueFalseSECRET_KEY硬编码环境变量读取ALLOWED_HOSTS空填实际域名数据库SQLitePostgreSQL/MySQL静态文件runserver 托管Nginx/对象存储最后说个习惯每次改完模型先makemigrations看生成的迁移文件再migrate别直接一把梭。我踩过一次坑字段改名时 Django 默认生成的是「删旧列加新列」数据全丢正确做法是在迁移里用RenameField。这个后悔药没有只能靠看迁移文件提前发现。希望帮到你。本文还有配套的精品资源点击获取