公司动态

Django完整版电商开发实战:从模型设计到部署上线

📅 2026/9/2 8:19:14
Django完整版电商开发实战:从模型设计到部署上线
简介一套以Django框架构建的完整电商系统源码项目基于Python 3.6环境实现了商家入驻与审核、商品发布管理、会员注册及手机验证、购物车增减、订单结算、模拟支付和省市区街道四级联动地址选择等核心业务。代码遵循MVT设计模式通过ORM简化数据库操作前端基于Bootstrap适合正在学习Django Web开发的初学者和需要快速搭建电商原型的开发者参考。压缩包共561个文件以70个py源码、55个pyc编译文件为主辅以37个js脚本、18个html页面、16个css样式以及大量jpg、png图片素材整体大小96.63MB文件类型覆盖了项目运行、界面展示与交互逻辑所需的主要资源。目前已有1771人学习下载源码可直接运行能够帮助读者完整理解从数据建模、视图逻辑、模板渲染到订单支付流程的Django全栈实现思路也可作为毕业设计或课程项目的改造基础。 看到这个标题我大概能猜到你准备入坑Django电商开发了——“django框架下开发的完整版的电商”这句话听起来简单但真正落地其实是一整套工程。我在实际项目中用Django做过不止一套电商系统从商品发布、购物车到订单履约、支付回调、后台运营管理每一步都埋着不少细节。这篇文章不会去贴大段官方文档而是按我真实做项目时的思考路径来拆把模块划分、模型设计、常见坑位、部署要点全部过一遍。不管你是刚学完Django基础想找个完整项目练手还是已经被安排了电商需求但心里没底这篇对你的参考价值都很大。1. 项目整体设计与思路拆解1.1 “完整版电商”不等于是把一个系统做成一坨电商项目最大的误区就是“我什么功能都要做”。商品列表、搜索排序、购物车、优惠券、秒杀、支付、物流、评价、退款售后、用户积分……你要是上来就把这些铺开项目大概率做不完而且维护成本极高。我自己的做法是先把电商系统的核心链路拆出来我会加粗一条主线商品上架 → 用户浏览 → 加入购物车 → 创建订单 → 支付 → 订单状态流转。这个主流程之外的功能属于支线先规划但不一定首期实现。所谓“完整版”指的应该是主流程从用户到后端再到管理后台全部跑通而不是功能无限堆叠。按这个逻辑我会把项目拆成四个业务appgoods商品与类目、品牌、SKU、库存users用户注册登录、收货地址、收藏trade购物车、订单、支付与退款coupon优惠券、后期可以扩展营销活动这样拆的好处很明显后期新增功能时不需要改老代码的核心模型每个人维护各自的app边界代码结构一眼能看明白。我第一次做电商时把所有模型都塞在同一个models.py里改一个字段牵一发动全身后来拆开才舒坦。如果你刚开始写不要嫌拆app麻烦这个动作后面会帮你省下大量时间。1.2 为什么选Django做电商后端这不是凑热门而是电商业务对后端框架有比较硬性的要求自带Admin后台电商系统离不开运营后台Django Admin可以让你在几个小时内搭出可用的后台管理界面商品、订单、用户都能直接维护这在项目前期非常重要。ORM强大电商涉及的查询非常复杂多表联查、聚合统计、分页排序Django ORM加select_related、prefetch_related用得熟练能覆盖绝大部分场景不需要写裸SQL。安全默认值高电商涉及资金交易和用户隐私Django默认开启CSRF防护、SQL注入保护、XSS过滤你不用额外做太多工作就能达到一个基础的合规水准。开发效率高一个商品的增删改查用DRFDjango Rest Framework写API配合Django的表单校验和序列化器前后端联调非常快。当然Django不是没有短板比如对于高并发场景它的同步模型确实不如Go或Node.js那套轻量但绝大多数电商系统瓶颈都在数据库和缓存层框架本身很少成为天花板。项目落地阶段用Django做后端是性价比很高的选择。2. 核心数据模型设计与实现2.1 app创建与业务边界划分开项目第一步是创建目录和app。命令很基础但怎么组织才是关键。我会用一个统一的命令序列把项目搭起来django-admin startproject ecommerce cd ecommerce python manage.py startapp goods python manage.py startapp users python manage.py startapp trade python manage.py startapp coupon创建完app后记得在INSTALLED_APPS里依次注册否则makemigrations不会识别模型。这里提醒一个新手常犯的问题app名称尽量用复数或业务名词不要叫shop、core这种太宽泛的名字业务名词能帮你明确聚合根的边界。usersapp还要做一件关键事在settings.py中指定自定义用户模型AUTH_USER_MODEL users.User最好在一开始就做这个配置因为Django默认的User模型字段太少了电商用户一般需要手机号、昵称、头像、性别、积分等字段后期再换自定义模型会非常痛苦。这个配置要在第一次migrate之前完成否则数据库结构已经固定再改容易出问题。2.2 商品模型与SKU设计电商商品模型最核心的是**SPUStandard Product Unit和SKUStock Keeping Unit**的区分。SPU是商品概念比如“iPhone 15 Pro”SKU才是具体可购买的版本比如“iPhone 15 Pro 256G 黑色”。如果这两层不分开后面库存、价格、规格会乱成一锅粥。我项目里的简化模型长这样from django.db import models class Category(models.Model): name models.CharField(max_length50, uniqueTrue, verbose_name类目名) parent models.ForeignKey(self, nullTrue, blankTrue, on_deletemodels.CASCADE, verbose_name父级类目) class Product(models.Model): name models.CharField(max_length200, verbose_name商品名称) category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name类目) sales models.IntegerField(default0, verbose_name累计销量) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Sku(models.Model): product models.ForeignKey(Product, related_nameskus, on_deletemodels.CASCADE, verbose_name所属商品) spec models.CharField(max_length100, verbose_name规格描述) price models.DecimalField(max_digits10, decimal_places2, verbose_name售价) stock models.PositiveIntegerField(default0, verbose_name库存) image models.ImageField(upload_toskus/%Y/%m/, blankTrue, verbose_name规格图)重点说一下字段选择price必须用DecimalField不要用FloatField。浮点数在二进制中无法精确表示比如0.1 0.2可能会变成0.30000000000000004金额计算出这种问题在电商平台上是致命的。stock用PositiveIntegerField从数据库层面保证不能为负数但这个约束在并发高时不够用后面下单时还要配合数据库锁。Category中的parent自关联用于支持多级类目这样不用为每个层级建单独的表。on_deletemodels.PROTECT用在类目上防止商品还在挂着的时候类目被误删。2.3 订单模型与状态管理订单是电商系统里最复杂的模型因为它同时关联用户、商品快照、地址、金额明细、支付流水而且订单状态是不断流转的。class Order(models.Model): ORDER_STATUS ( (PENDING_PAYMENT, 待支付), (PAID, 已支付), (SHIPPED, 已发货), (COMPLETED, 已完成), (CANCELLED, 已取消), ) order_no models.CharField(max_length32, uniqueTrue, verbose_name订单号) user models.ForeignKey(users.User, on_deletemodels.PROTECT, related_nameorders, verbose_name下单用户) total_amount models.DecimalField(max_digits10, decimal_places2, verbose_name订单总额) status models.CharField(max_length20, choicesORDER_STATUS, defaultPENDING_PAYMENT, db_indexTrue, verbose_name订单状态) receiver_name models.CharField(max_length50, verbose_name收货人) receiver_phone models.CharField(max_length11, verbose_name收货电话) receiver_address models.CharField(max_length200, verbose_name收货地址) created_at models.DateTimeField(auto_now_addTrue, verbose_name下单时间) paid_at models.DateTimeField(nullTrue, blankTrue, verbose_name支付时间) class OrderItem(models.Model): order models.ForeignKey(Order, related_nameitems, on_deletemodels.CASCADE, verbose_name所属订单) sku models.ForeignKey(goods.Sku, on_deletemodels.PROTECT, verbose_name商品SKU) product_name models.CharField(max_length200, verbose_name商品快照名称) price models.DecimalField(max_digits10, decimal_places2, verbose_name成交价) quantity models.PositiveIntegerField(default1, verbose_name数量)这里有个细节值得注意OrderItem里存了product_name和price这叫商品快照。为什么要冗余因为用户下单后商品可能会改名、下架甚至改价但订单里的交易信息必须保持下单时的状态否则对账和售后都说不清。这个冗余字段看似重复实际是业务上的刚需。订单号不建议用自增ID因为会暴露平台真实销量而且多系统交互时容易撞号。我一般用时间戳加随机数生成YYYYMMDDHHMMSS 用户ID 4位随机数。为了在并发时不重复order_no字段必须加uniqueTrue创建时偶发冲突就重试一次。状态管理方面不要只靠改字段值支付回调成功后要写一条流水日志比如谁在什么时候把状态从“待支付”改成“已支付”操作人是谁。这样出问题可以追溯。3. 关键功能实现与踩坑记录3.1 文件上传与图片处理开发时很快上线后掉链子商品肯定要传图片Django帮我们封装了ImageField但实际用起来有几个坑。我在项目里这样配置图片上传先确保依赖装了因为ImageField依赖Pillow没安装的话迁移直接报错。pip install Pillow然后settings.py里配置MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media模型里定义好上传字段后如果是在Django Admin里维护图片上传直接就能用了。但如果你是写API接口需要接收上传文件视图代码要注意处理request.FILES# 简化示例使用DRF序列化器接收图片 class ProductImageUploadSerializer(serializers.ModelSerializer): class Meta: model ProductImage fields [sku, image] def validate_image(self, value): # 限制文件大小不能超过5MB if value.size 5 * 1024 * 1024: raise serializers.ValidationError(图片大小不能超过5MB) # 技术文件类型 valid_types [image/jpeg, image/png, image/webp] if value.content_type not in valid_types: raise serializers.ValidationError(只支持jpg/png/webp格式) return value我这里补充一个生产中容易忽略的点upload_to一定按时间分目录比如skus/%Y/%m/这样图片分散在不同月份的目录下单目录文件数可控后端备份和清理也比较方便。不要把所有图片都塞到一个目录里文件多了之后访问和备份都会变慢。关于文件类型校验不要只看扩展名。有人传一个shell.jpg实际是PHP脚本或HTML文件这种文件如果被Nginx当静态文件服务了可能存在安全风险建议加上content_type校验和文件头检查。用Django本身的validate_image_file_extension也能挡掉一部分但还不够代码里手动校验一次心里踏实。3.2 购物车与订单流程库存扣减必须要加锁购物车实现有两种方案存Cookie和存数据库。我的建议是未登录用户用Cookie购物车登录后进入数据库购物车。但实际上小项目里为了省事直接在服务端存购物车表也可以因为一旦用户需要跨设备同步购物车Cookie方案就要重写。购物车的模型就三件事用户、SKU、数量这个很简单。复杂的是从购物车创建订单的过程这里的库存扣减是电商并发问题最集中的地方。我最早实现时写的代码是sku Sku.objects.get(idsku_id) if sku.stock quantity: sku.stock - quantity sku.save()这段代码在并发场景下必出事故。两个请求同时读到stock10都判断库存够都执行扣减最后库存可能变成-8或者实际卖出去的数量超过库存。正确做法是用Django提供的select_for_update()行锁from django.db import transaction with transaction.atomic(): sku Sku.objects.select_for_update().get(idsku_id) if sku.stock quantity: raise APIException(库存不足) sku.stock - quantity sku.save() # 在这里继续创建订单整个流程在一个事务里select_for_update()会锁住这行直到事务结束后到的请求只能等前一个执行完。这样库存就不会超卖。注意select_for_update()必须在事务里才生效所以外层要套transaction.atomic()。3.3 用户认证与权限控制Django内置的登录认证很完善但电商要扩展。我用的是AbstractUser扩展字段同时使用JWT做接口认证。这样Web前端和移动端都能复用同一套账号体系。权限控制方面区分普通用户和管理员是电商的安全底线。普通用户只能查自己的订单不能查别人的管理员才有权限操作商品上下架、订单发货。class IsOwnerOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True # 订单只能由本人操作管理员走后台 return obj.user request.user我发现不少刚做电商的同学上来就对每个视图写一堆if request.user.is_authenticated的判断这样零散又容易漏。建议直接用DRF的权限类集中管理一个类加在视图上比到处写条件判断干净得多。4. 后台管理、前后端整合与性能优化4.1 Django与Vue整合两种模式按项目规模选热词里看到了“django vue整合”这也是电商项目常见的组合。Django做后端API、Vue做前端页面两者的整合有两种主流方式模式一前端构建后由Django托管将Vue项目npm run build之后把dist目录的内容拷贝到Django的static目录或templates目录由Django统一托管。这种模式适合前后端由同一个人维护、部署环境简单的场景一个Nginx站点就搞定。模式二完全前后端分离Django只负责提供APIDRFVue独立部署在Nginx上通过/api路径或跨域访问后端接口。这种模式是电商项目的标配适合团队协作、有独立前端开发任务的场景。我做项目偏好第二种因为前端发布和后端发布互不干扰出了问题也容易定位。但如果你只是个人练习第一种省事得多。无论哪种模式后端API都应该保持纯粹不要在前端页面里塞太多Django模板语法否则前后端逻辑就混在一起了。4.2 让Django Admin成为运营后台的利器Django的Admin是电商项目选型时被严重低估的功能。很多教程只用默认的注册方式实际上把Admin配置好了能省掉一个独立的运营后台开发成本。admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display [order_no, user, total_amount, status, created_at] list_filter [status, created_at] search_fields [order_no, user__username, receiver_name] ordering [-created_at] # 对于金额字段设置为只读防止运营误改金额 readonly_fields [total_amount, order_no]实战中Admin里最常用的技巧是list_display把最重要的字段显示在列表页一眼掌握订单全貌。list_filter按状态筛选运营每天操作最多的就是查看待发货订单。search_fields支持跨表搜索比如user__username客服找单子会很快。inlines用来在一个页面上同时操作主表和子表比如订单页直接看到所有商品明细非常直观。不要给普通运营开Admin的全局权限只给指定模型的管理权限或者更稳妥的是只用自定义的Django Admin站点把管理界面和用户端隔离。我遇到过不少团队不晓得Django有Admin就去专门开发了一套后台管理开发周期至少多两周。如果你要快速交付一个电商项目Admin用起来这是Django实际提高交付效率最明显的地方。4.3 查询性能优化Preload的必要性Django默认是懒加载访问外键时才会再查一次数据库如果列表页循环里有10条订单、每条订单查20个关联对象就会产生200条SQL。这个问题在电商后台尤其明显商品列表、订单列表非常容易把数据库拖死。解决方案很简单查询时用select_related针对外键、一对一orders Order.objects.select_related(user).all()用prefetch_related针对多对多、反向关联orders Order.objects.prefetch_related(items__sku).all()加上之后数据库查询数量从几百条降到几条。电商项目的性能优化第一步永远是看ORM有没有产生N1查询这是性价比最高的方式。除此之外Redis缓存热门商品的详情、在列表页缓存前几页的数据也能明显降低数据库压力。5. 部署上线与常见问题5.1 用宝塔面板部署Django的完整步骤项目开发完之后部署上线又是一个关卡。我最近几次部署用的都是宝塔面板它对Python项目的支持比较成熟步骤如下在宝塔中安装Python项目管理器创建一个新项目Python版本选3.10或更高。把代码上传到服务器创建虚拟环境安装依赖pip install -r requirements.txt安装gunicorn用Gunicorn启动Django应用gunicorn ecommerce.wsgi:application -b 127.0.0.1:8000 --workers3 --timeout60workers数量一般设为CPU核心数 * 2 1比如2核4G服务器就设5个左右太多了反而会因为上下文切换降低效率。在Nginx里配置反向代理server { listen 80; server_name your_domain.com; location /static/ { alias /www/wwwroot/your_project/static/; } location /media/ { alias /www/wwwroot/your_project/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里有两个容易踩的坑部署前一定要执行python manage.py collectstatic收集静态文件否则Django Admin的CSS、JS全部加载不出来。settings.py里要把DEBUG False同时设置ALLOWED_HOSTS [你的域名]否则访问会被拒绝。数据库建议用MySQL而不是默认的SQLite。上线后我用MySQL才意识到SQLite在并发写入时容易锁库电商场景数据量一大就顶不住。你可以用宝塔一键装好MySQL然后修改DATABASES配置再用makemigrations和migrate同步表结构。5.2 线上常见问题与排查速查表电商项目上线后问题往往集中在下面几个地方我整理成一张排查表每个问题后面是我实际用的排查思路。问题现象大概率原因排查方法用户头像/商品图片打不开Nginx没配/media/代理或MEDIA_ROOT路径不对先访问/media/xx.jpg确认报错类型再看Nginx日志Django Admin样式全丢没执行collectstatic执行命令确认STATIC_ROOT和Nginx静态目录指向同一路径数据库连接数超限MySQL最大连接数默认偏小而Django每条连接不释放调大max_connections或使用连接池工具订单状态一直显示待支付支付回调地址没有配置到公网可达的URL检查支付平台异步通知地址不能用内网IP接口访问很慢框架代码N1查询或Redis未缓存热点数据开Django Debug Toolbar查看SQL次数定位后优化我一般排查问题时有个习惯先把DEBUG打开临时看错误栈定位完立刻关掉。生产环境日志必须记录到文件前端调用接口出错时看Django的日志文件比前端控制台报错信息要完整得多。最后分享一下我做这套项目的心得做Django电商项目最大的感受到后期越明确千万不要被“完整版”三个字迷惑先把从用户下单到支付成功这条主链路跑通再去填支线功能。模型设计上用户模型和订单模型一定要留扩展空间但功能上要克制先做最少可行版本。我最初设计订单表时加了20多个字段最后实际用上的只有一半后期的业务调整反而被多余字段绑住了手脚。电商系统对细节的要求比普通管理系统高很多库存扣减、金额精度、订单状态流转这些地方每一步都要想清楚为什么这么设计。如果能从头再做一遍我会把更多精力放在接口的健壮性上而不是堆功能因为线上出问题时能快速定位并恢复才是最重要的。本文还有配套的精品资源点击获取