简介:这是一套基于Python Django框架开发的完整电商商城项目源码,面向Web后端初学者与Django进阶开发者,可用于学习典型B2C商城的核心业务逻辑与工程实践。资源包含403个文件,主体为53个Python后端代码文件(含models、views、urls等模块)、11个HTML模板页、8个CSS样式文件(如proList.css、detail.css、login.css等体现多页面布局)、以及293张商品图片素材,整体压缩包33.44MB,结构清晰,覆盖用户认证、商品展示、购物车、订单管理等关键功能模块。已有3684人下载学习,适合通过真实项目理解Django MTV架构、ORM建模、模板渲染、静态资源组织及前后端协同开发流程,是开展课程设计、毕业设计或技术面试准备的实用参考范例。
1. 项目概述:一个Django商城项目的核心价值
最近在整理硬盘,翻出来一个几年前用Django写的商城项目源码,打包成了商城项目源码.zip。这个项目虽然不是什么惊天动地的架构,但麻雀虽小五脏俱全,从用户注册登录到商品下单支付,再到后台管理,整个电商的核心链路都跑通了。对于想从零开始理解一个Web应用是如何构建,特别是想深入Django这个“大而全”框架的朋友来说,这个源码包的价值可能比看十篇教程都来得实在。Django在国内Web开发领域的应用非常广泛,尤其是在需要快速构建稳健后台管理系统的场景下,它的ORM、Admin、表单和认证系统能帮你省下大量重复造轮子的时间。这个项目就是一个典型的例子,它没有追求最新最炫的技术栈,而是扎扎实实地用Django的核心组件解决了一个商城系统的基本问题。无论你是刚学完Python语法想找个实战项目练手,还是已经有一定基础想看看一个完整项目的代码组织逻辑,这份源码都能提供一个清晰的参考。
2. 项目整体架构与设计思路拆解
2.1 技术选型与架构模式
这个项目采用了经典的MVC(在Django中更准确地说是MTV:Model-Template-View)架构模式。选择Django而非其他框架如Flask或FastAPI,核心考量在于其“开箱即用”的特性。对于一个商城系统,用户认证、后台管理、表单处理、数据库ORM都是刚需,Django内置的这些组件成熟稳定,能让我们把精力集中在业务逻辑而非基础设施上。
后端框架自然是Django,版本锁定在3.2 LTS,这是一个长期支持版本,在稳定性和社区支持上找到了很好的平衡点。数据库选择了MySQL 5.7/8.0,考虑到商城业务涉及较多的事务和关联查询,关系型数据库依然是可靠的选择。前端没有采用前后端分离的复杂架构,而是使用了Django原生的模板引擎(Django Templates)配合Bootstrap 5来构建页面。这种选择对于中小型、以内容展示和表单交互为主的商城项目来说,开发效率极高,避免了分离部署、接口联调等额外复杂度。静态资源使用Whitenoise中间件在本地和生产环境提供高效服务。
项目结构清晰,遵循了Django的最佳实践。根目录下除了标准的manage.py和配置文件,主要应用(App)按功能模块划分:
users: 处理用户注册、登录、个人中心、收货地址管理。goods: 核心的商品模块,包括商品分类、品牌、SPU(标准产品单元)、SKU(库存量单位)模型,以及商品列表、详情页的展示逻辑。orders: 订单模块,涵盖购物车、订单创建、状态流转、支付回调(集成模拟支付)和订单管理。payments: 支付模块,抽象了支付网关接口,目前实现了支付宝沙箱和微信支付模拟接口,便于理解支付流程。utils: 存放公共工具函数,如自定义的装饰器、验证码生成、短信发送(模拟)客户端等。
2.2 核心数据模型设计解析
数据模型是任何应用的基石,商城系统的模型设计尤其关键。在这个项目中,我重点设计了几个核心模型,并充分考虑了扩展性。
首先是用户模型UserProfile。我没有直接使用Django内置的AbstractUser进行简单扩展,而是采用了与内置User模型一对一关联(OneToOneField)的方式。这样做的好处是既可以利用Django强大的原生认证系统(auth模块),又可以为商城用户定制大量额外字段(如昵称、头像、性别、生日、默认收货地址等),而不会污染原始的auth_user表结构。这种设计在需要频繁升级Django版本或与其他使用标准用户模型的App集成时,灵活性更高。
商品模型的设计是另一个重点,采用了电商领域常见的SPU+SKU模式。GoodsSPU表定义了一个标准产品的抽象信息,如商品名称、副标题、商品描述、所属分类和品牌。GoodsSKU表则定义了具体的销售属性,如颜色、尺寸、规格,以及独立的价格、库存、销量和上下架状态。一个SPU对应多个SKU。这种设计将商品的基本信息与销售属性解耦,既能高效管理商品公共信息,又能灵活应对多规格商品的库存和定价。分类GoodsCategory采用了自关联(parent_category字段)实现无限级分类,并通过db_index=True为频繁查询的字段建立索引。
订单模型OrderInfo和订单商品模型OrderGoods构成了订单系统的核心。OrderInfo记录了订单的宏观信息:订单号(唯一)、用户、总金额、支付方式、订单状态(使用choices选项定义,如“待支付”、“待发货”、“待收货”、“已完成”、“已取消”)、收货地址快照等。OrderGoods则是一个关联表,记录了订单中每个SKU商品的购买数量、成交单价(下单时快照,与商品实时价格解耦)和评价状态。这种设计确保了订单数据的不可变性,即使用户后来修改了收货地址或商品价格发生了变化,历史订单信息依然保持不变。
注意:在模型字段设计时,对于金额类字段(如商品价格、订单总价),务必使用
DecimalField并指定精确的max_digits(总位数)和decimal_places(小数位数),例如max_digits=10, decimal_places=2。绝对不要使用FloatField,因为浮点数在计算和存储时可能存在精度丢失问题,这在金融相关的业务中是致命的。
3. 核心功能模块的详细实现与实操要点
3.1 用户系统:从注册登录到个人中心
用户模块是流量的入口。注册功能除了常规的用户名、密码、手机号验证,我还集成了图形验证码和短信验证码(模拟)来防止恶意注册。验证码的生成使用Pillow库绘制,并将验证码文本存入Redis或Session中用于校验。短信服务则对接了一个模拟接口,在实际部署时,可以轻松替换为阿里云、腾讯云等提供的短信服务SDK。
登录功能直接使用了Django内置的authenticate()和login()函数,安全可靠。但有一个关键细节:在登录视图(View)中,我不仅校验用户名密码,还检查了用户是否被激活(is_active)以及是否被后台管理员禁用。这为后续可能需要的用户管理提供了钩子。登录成功后,使用Django的@login_required装饰器来保护需要登录才能访问的视图,例如个人中心、收货地址管理页面。
个人中心页面展示了用户的基本信息、订单概览和收货地址列表。收货地址管理实现了增删改查全套操作,并允许用户设置一个默认地址。在创建或编辑地址时,前端通过三级联动选择省市区,这里我预先将全国行政区划数据导入到了一个单独的Area模型中,通过Ajax异步加载实现无刷新联动,提升了用户体验。
3.2 商品展示系统:列表、详情与搜索
商品列表页是商城的门面。视图(View)中,我首先处理了查询参数:分类ID、排序方式(按价格、销量、上新)、页码。通过Django ORM的select_related和prefetch_related方法,我有效地避免了在模板中渲染商品时因外键关联而产生的“N+1查询问题”。例如,在获取商品列表时,一次性通过select_related('category', 'spu')将分类和SPU信息关联查询出来,而不是在循环中逐条查询。
商品详情页除了展示SKU的详细信息、图片轮播、规格参数外,最关键的是库存判断。当用户选择不同规格(如颜色、尺寸)时,前端通过Ajax请求后端,后端根据SKU ID实时查询库存并返回。库存数量是电商系统的生命线,所有涉及库存增减的操作(如下单、取消订单、后台修改)都必须放在数据库事务中处理,并使用F()表达式进行原子操作,防止超卖。例如,在用户下单时:
from django.db import transaction, models with transaction.atomic(): # 1. 创建订单保存点 sid = transaction.savepoint() try: # 2. 查询SKU并判断库存(使用select_for_update加行锁,防止并发修改) sku = GoodsSKU.objects.select_for_update().get(id=sku_id, is_launched=True) if sku.stock < buy_count: transaction.savepoint_rollback(sid) return JsonResponse({'code': 400, 'errmsg': '库存不足'}) # 3. 使用F表达式原子更新库存和销量 GoodsSKU.objects.filter(id=sku_id).update( stock=models.F('stock') - buy_count, sales=models.F('sales') + buy_count ) # 4. 创建订单记录... transaction.savepoint_commit(sid) except Exception as e: transaction.savepoint_rollback(sid) # 记录日志并返回错误搜索功能基于Django的Q对象实现简单的多条件过滤。对于更复杂的全文搜索需求,项目预留了接口,可以方便地集成像django-haystack+Whoosh或Elasticsearch这样的专业搜索引擎。
3.3 购物车与订单流程的完整实现
购物车设计采用了混合方案:用户未登录时,购物车数据存储在浏览器的localStorage中;用户登录后,则同步到服务器端的数据库Cart表中。这样做既保证了未登录用户的体验,又实现了登录后数据的持久化和多端同步。购物车表Cart很简单,主要字段是用户、SKU、商品数量和添加时间。
订单流程是电商最核心、最复杂的链路,我将其拆解为几个清晰的步骤,并在视图函数中严格校验每一步:
- 订单确认页:用户从购物车选择商品进入。视图需要校验:所选SKU是否有效、库存是否充足、用户是否有默认收货地址。这里会计算商品总价、运费,生成一个订单总金额的预览。所有价格信息都是从数据库实时查询并计算的,确保准确性。
- 订单创建:用户提交订单。这是整个系统里对数据一致性和事务性要求最高的地方。我使用了Django的
transaction.atomic()装饰器来确保以下操作在一个数据库事务中完成:- 从购物车中取出选中的商品。
- 遍历每个商品,使用
select_for_update()锁定SKU行,再次检查库存。 - 使用
F()表达式原子性地减少库存、增加销量。 - 生成唯一的订单号(采用“时间戳+用户ID+随机数”的算法)。
- 创建
OrderInfo主订单记录和多个OrderGoods子订单记录。 - 将购物车中对应的商品项删除。 任何一步失败,整个事务都会回滚,库存和销量数据保持不变,保证了数据的强一致性。
- 订单支付:订单创建成功后,跳转到支付选择页。项目集成了支付宝和微信支付的模拟流程。以支付宝为例,视图会调用支付宝提供的Python SDK,生成一个包含订单号、金额、商品描述等信息的支付请求参数,前端使用这些参数跳转到支付宝的支付页面(或唤起手机支付宝App)。支付成功后,支付宝会异步通知(回调)我们指定的一个后端接口
/payments/alipay/notify/。这个回调接口的处理至关重要:必须验证支付宝传来的签名,确保通知的真实性;然后根据通知中的订单号更新本地订单状态为“已支付”;最后返回一个success字符串给支付宝,否则支付宝会认为通知失败而反复重试。 - 订单状态管理:用户可以在个人中心查看订单列表和详情。后台管理员则可以通过Django Admin或自定义的管理后台操作订单状态(发货、完成、取消等)。每次状态变更都应记录日志,并可以考虑通过消息队列异步发送通知(如短信、邮件)给用户。
实操心得:在开发支付回调接口时,一定要做好日志记录,将支付宝/微信支付回调过来的所有参数,无论是成功还是失败,都详细记录下来。因为支付环境复杂,网络抖动、用户中途关闭页面等都可能导致问题。有了完整的日志,当用户投诉“付了款但订单没更新”时,你才能快速定位是支付平台的通知没发出来,还是我们的接口处理失败了。此外,回调接口的代码必须做幂等处理,即同一条支付成功的通知,即使因为网络原因被重复调用多次,最终结果也应该是订单只被标记为支付成功一次,避免重复给用户加积分或发货。
4. 后台管理系统的快速搭建与深度定制
Django Admin是这个项目的一大亮点,它几乎零代码就为我们生成了一个功能强大的商品和订单管理后台。但默认的Admin往往不能满足实际业务需求,需要进行深度定制。
4.1 基础模型注册与列表优化
首先,在各自App的admin.py文件中,使用admin.site.register(YourModel)进行注册。但很快你会发现默认的列表页只显示对象的字符串表示,信息量很少。这时可以通过定义ModelAdmin类来定制:
from django.contrib import admin from .models import GoodsSKU @admin.register(GoodsSKU) class GoodsSKUAdmin(admin.ModelAdmin): # 列表页显示的字段 list_display = ['id', 'name', 'spu', 'category', 'price', 'stock', 'sales', 'is_launched'] # 支持直接编辑的字段(无需进入详情页) list_editable = ['price', 'stock', 'is_launched'] # 右侧过滤器 list_filter = ['category', 'is_launched', 'create_time'] # 搜索框支持的字段 search_fields = ['name', 'spu__name', 'id'] # 每页显示数量 list_per_page = 50这样,管理员就能在一个页面里快速浏览、搜索、筛选和编辑大量商品SKU了。
4.2 复杂表单与内联编辑
对于商品SPU(GoodsSPU),它下面有多个SKU,我们希望在编辑SPU的同时,能直接编辑或新增其关联的SKU。这就要用到TabularInline或StackedInline。
class GoodsSKUInline(admin.TabularInline): model = GoodsSKU extra = 1 # 默认显示一个空白的SKU表单 fields = ['name', 'price', 'stock', 'sales', 'spec'] @admin.register(GoodsSPU) class GoodsSPUAdmin(admin.ModelAdmin): inlines = [GoodsSKUInline] # ... 其他配置现在,在SPU的编辑页面,下方会以一个表格的形式列出所有关联的SKU,并可以直接修改或新增,管理效率大大提升。
4.3 自定义Action与批量操作
后台经常需要执行批量操作,比如将选中的商品批量上架或下架。Django Admin可以很方便地定义自定义Action。
def make_launched(modeladmin, request, queryset): queryset.update(is_launched=True) make_launched.short_description = "批量上架选中的商品" def make_unlaunched(modeladmin, request, queryset): queryset.update(is_launched=False) make_unlaunched.short_description = "批量下架选中的商品" class GoodsSKUAdmin(admin.ModelAdmin): actions = [make_launched, make_unlaunched] # ... 其他配置这样,管理员在商品列表页勾选多个商品后,就可以从Action下拉菜单中执行批量操作。
4.4 字段重写与富文本编辑器集成
商品描述通常需要富文本编辑。Django Admin默认是普通文本框,我们可以集成第三方编辑器,比如django-ckeditor。安装配置后,只需在ModelAdmin中重写对应字段的form即可:
from django import forms from ckeditor.widgets import CKEditorWidget class GoodsSPUAdminForm(forms.ModelForm): description = forms.CharField(widget=CKEditorWidget()) class Meta: model = GoodsSPU fields = '__all__' class GoodsSPUAdmin(admin.ModelAdmin): form = GoodsSPUAdminForm现在,SPU的描述字段就变成了一个功能强大的富文本编辑器。
5. 项目部署、性能优化与安全加固实录
5.1 从开发到生产:关键配置迁移
开发时我们使用Django自带的开发服务器和SQLite,但生产环境完全不同。首先,在settings.py中通过环境变量区分不同配置。关键改动包括:
- 数据库:切换到MySQL或PostgreSQL,配置连接池(如使用
django-db-connections)。 - 密钥与调试:
SECRET_KEY必须从环境变量读取,绝对不能硬编码在代码中。DEBUG必须设置为False。 - 静态文件:使用
python manage.py collectstatic收集所有静态文件到指定目录(如/static/),然后通过Nginx直接代理,或者使用白名单中间件Whitenoise(对于中小流量项目足够)。 - Allowed Hosts:必须设置
ALLOWED_HOSTS = ['你的域名', '你的服务器IP'],否则Django会拒绝服务。 - 中间件:生产环境需要添加安全相关的中间件,如
SecurityMiddleware(自动添加安全头部)、SessionMiddleware(配置为使用数据库或Redis缓存存储Session)。
5.2 性能优化实战技巧
随着商品和用户量增长,性能瓶颈会逐渐暴露。以下是我在实践中总结的几个优化点:
- 数据库查询优化:这是最常见的瓶颈。持续使用
django-debug-toolbar监控页面产生的SQL查询。坚决杜绝N+1查询,善用select_related(用于一对一、多对一正向关联)和prefetch_related(用于多对多、一对多反向关联)。对于复杂的聚合查询,考虑使用annotate和aggregate在数据库层面完成计算。 - 缓存策略:Django提供了强大的缓存框架。我将以下内容纳入了缓存:
- 全站缓存:对于变化不频繁的页面,如商品详情页(在商品信息更新时手动清除缓存),可以使用
CacheMiddleware进行全页面缓存。 - 片段缓存:在模板中使用
{% cache 300 sidebar %}...{% endcache %}缓存页面中某个部分,如商品分类导航栏。 - 数据缓存:使用
from django.core.cache import cacheAPI缓存昂贵的查询结果,如首页的热销商品排行榜。缓存后端可以配置为Redis或Memcached。
- 全站缓存:对于变化不频繁的页面,如商品详情页(在商品信息更新时手动清除缓存),可以使用
- 静态文件与媒体文件:务必使用CDN分发静态文件(CSS, JS, 图片)。用户上传的媒体文件(商品图、用户头像)建议存储到对象存储服务(如阿里云OSS、腾讯云COS),它们带宽大、成本低、有图片处理能力(缩略图、水印)。
5.3 安全加固清单
安全无小事,尤其是涉及支付和用户数据的商城系统。
- CSRF保护:Django默认已开启,确保所有POST表单都包含
{% csrf_token %}。 - XSS防护:Django模板默认自动转义HTML标签。但如果某些字段需要富文本,在渲染时必须使用
|safe过滤器,并确保输入时已经过清洗(如使用bleach库)。 - SQL注入:坚持使用Django ORM或参数化查询,基本可以免疫。
- 敏感信息:密码必须使用
make_password哈希存储,绝对禁止明文。数据库连接密码、第三方API密钥等必须通过环境变量配置。 - 文件上传:限制上传文件的类型(通过MIME类型和后缀名双重校验)、大小,并对图片进行重命名(如使用UUID),防止用户上传恶意文件或通过路径遍历攻击服务器。
- 权限控制:除了前端的登录检查,后端每个视图函数都要进行权限校验。使用
@login_required、@permission_required装饰器,或自定义装饰器检查用户角色。
5.4 常见问题与排查技巧实录
在开发和维护这个项目的过程中,我踩过不少坑,也总结了一些排查问题的经验。
问题一:静态文件在开发环境正常,部署后404。
- 排查:首先检查
settings.py中的STATIC_URL和STATIC_ROOT配置。STATIC_ROOT是执行collectstatic后文件收集的目录,需要确保Nginx或Apache的配置正确指向了这个目录,或者STATICFILES_DIRS配置正确。使用python manage.py findstatic css/style.css命令可以查找Django认为的静态文件路径。 - 解决:对于使用Nginx的情况,在配置文件中添加一个location块:
location /static/ { alias /path/to/your/static_root/; }。确保Nginx用户对该目录有读取权限。
问题二:使用F()表达式更新后,从数据库重新取出的值好像没变?
- 排查:这是一个常见的误解。
F()表达式更新是在数据库层面进行的原子操作,但更新后,当前Python内存中的模型实例对象(obj)的字段值并不会自动刷新。 - 解决:如果需要获取更新后的值,必须从数据库重新加载该对象:
obj.refresh_from_db()。
问题三:支付回调接口被重复调用,导致订单状态异常更新。
- 排查:检查支付回调接口的日志,看是否收到了多条内容相同的POST请求。这通常是支付平台的网络重试机制导致的。
- 解决:实现接口的幂等性。在回调处理逻辑的最开始,根据支付平台传过来的唯一交易号(如支付宝的
trade_no)去查询本地是否已存在处理成功的记录。如果已存在,直接返回成功响应,不再执行后续的订单更新逻辑。可以在数据库中为这个交易号建立唯一索引,从数据库层面防止重复记录。
问题四:后台Admin操作速度很慢,特别是有关联数据的列表页。
- 排查:使用
django-debug-toolbar查看该Admin页面生成的SQL,很可能是因为在列表页显示外键字段(如list_display = ['spu'])时,每条记录都去单独查询了一次关联表。 - 解决:在自定义的
ModelAdmin中重写get_queryset方法,使用select_related提前加载关联数据:def get_queryset(self, request): return super().get_queryset(request).select_related('spu', 'category')。
这个Django商城项目源码,就像一本涵盖了需求分析、设计、编码、调试和部署思考的实战笔记。它可能没有用到最前沿的微服务或云原生架构,但其中对业务逻辑的梳理、对数据一致性的处理、对常见问题的规避方案,都是构建一个可靠Web应用的通用经验。代码是死的,但解决问题的思路是活的。希望你在阅读和运行这份代码时,能更关注“为什么这么设计”,而不仅仅是“怎么实现”。当你理解了背后的权衡与考量,也就具备了独立设计和开发更复杂系统的能力。
本文还有配套的精品资源,点击获取