
简介这是一套基于 Python Django 框架开发的商城管理系统完整项目内含源码与数据库适合高校学生用于课程设计或期末大作业也适合初学 Django 的开发者通过整站代码理解 Web 开发流程。项目已获导师指导并通过最终评分 97 分压缩包可直接下载使用运行环境配置好后即可展示完整页面无需修改。包内共 980 个文件大小约 52.67MB文件构成以图片素材和前端资源为主286 张 PNG、279 张 JPG 用于商品图片和界面设计111 个 JS 脚本与 72 个 CSS 样式实现前端交互和页面布局64 个 HTML 页面构成商城页面骨架33 个 Python 源码文件承载 Django 后端业务逻辑另有 SQLite 数据库文件和 SQL 脚本可快速还原数据覆盖商城常见的商品、购物车与订单模块。当前已有 272 人学习浏览说明这套代码在同类作业中具备一定参考价值。读者既可直接作为项目提交也可以按路由、模型、视图与模板的路径拆解学习项目目录结构清晰适合在此基础上做二次开发。1. 从 97 分的期末项目看 Django 商城该怎么做拿到这份 Django 商城管理系统源码时我第一时间翻的是项目里的静态文件目录——amazeui.min.css、bootstrap.css、ueditor.css 一应俱全。一个期末大作业能堆出这套资源说明作者没把精力浪费在重复造轮子上而是把 Django 的 MTV 架构、ORM 模型和后台管理这三块基本功吃透了。这个项目带完整数据库下载后配置好环境就能直接跑适合两类人一是正在做 Python 课程设计、Django 期末大作业的学生二是想快速搭一个可演示商城原型、用来学习 Django 业务闭环的开发者。先给结论这个项目能拿高分不是因为页面花哨而是它的数据表设计、后台配置和购物车逻辑踩在了 Django 最佳实践的节奏上。接下来按从骨架到血肉的顺序拆开讲。2. Django MTV 架构与商城模块划分2.1 先看懂 Django 的请求流转再决定代码放哪Django 的 MTV 模型很容易被新手误解成「MVC 换了个名字」但实际写起来差别很大。MVC 里 Controller 负责调度而在 Django 里这个角色被拆分成了 URLconf路由和 View 两层路由层做 URL 分发视图函数接收 HttpRequest、处理业务逻辑、返回 HttpResponse。模板 Template 只做展示模型 Model 只做数据映射。商城系统的核心链路——浏览商品、加购、下单——就是把这条流转路径走通。这个项目里静态资源bootstrap.css、amazeui.min.css直接放在 static 目录说明作者选择了「前端资源本地化」而不是 CDN 引用。这是个明智的期末作业策略演示时断网也能完整渲染页面导师打开本地环境不用等外网资源。make 注意Django 处理静态文件在开发模式和生产模式是两套逻辑后面第 5 章会专门讲 collectstatic 的坑。2.2 settings.py 里的关键配置INSTALLED_APPS 与数据库连接打开项目的 settings.py第一件事看 INSTALLED_APPS。一个规范的 Django 商城项目APP 列表至少包含以下内容INSTALLED_APPS [ django.contrib.admin, # 后台管理商城系统的管理端基石 django.contrib.auth, # 用户认证登录/登出/权限判断全依赖它 django.contrib.contenttypes, # auth 框架的依赖项不要删 django.contrib.sessions, # 会话支持购物车功能要用 session 或 cookie django.contrib.messages, # 后台操作后的提示消息 django.contrib.staticfiles, # 静态文件处理 myapp, # 商城主应用名称可能不同看你的项目 ]这段配置的核心逻辑是Django 的 auth 和 admin 是深度耦合的。如果你删掉了django.contrib.authadmin 后台立刻崩溃因为 admin 的 user 模型、权限校验完全建立在 auth 之上。很多期末项目「一运行就报错」八成是这里少了一个 app。数据库配置在 settings.py 底部的 DATABASES 字典期末项目最常见的组合是 SQLite 和 MySQL 二选一。项目自带数据库如果用的是 SQLite迁移文件里的数据可以直接用如果是 MySQL需要先建好同名数据库再导数据。下面以 MySQL 为例DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: shop_db, # 数据库名需提前 CREATE DATABASE USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, } }注意ENGINE结尾是mysql而不是mysqldb。因为 Django 2.2 之后默认依赖的 MySQL 驱动是mysqlclient安装命令是pip install mysqlclient。Windows 上装这个包经常报错常见做法是去https://www.lfd.uci.edu/~gohlke/pythonlibs/下载对应 Python 版本的 whl 文件再本地安装或者改用pymysql并在__init__.py里写pymysql.install_as_MySQLdb()。2.3 用 startapp 拆商城模块别把全部代码塞进一个文件打开项目根目录你会看到它至少有一个独立的应用目录比如myapp而不是把所有 models、views 全写在项目同名的配置目录里。这是 Django 项目的基本素养功能模块用python manage.py startapp 应用名创建一个应用聚焦一个业务域。商城系统一般这样拆python manage.py startapp goods # 商品模块商品信息、分类 python manage.py startapp cart # 购物车模块加购、数量修改 python manage.py startapp order # 订单模块订单创建与状态管理这个拆分逻辑不是拍脑袋。商品、购物车、订单三者的数据变更频率和业务边界都不同商品数据基本只读购物车是高频读写但生命周期短订单牵涉支付和状态流转。拆开后迁移文件互不干扰后期扩展比如加一个优惠券模块也能独立迭代。如果你拿到手的项目只用一个应用也别慌期末作业场景下模型层够用即可重点是理解每一行配置的含义。3. MySQL 建表与 ORM 模型设计3.1 商品表、分类表、购物车表的核心字段设计打开项目里的 models.py重点看三张核心表商品Product、分类Category、购物车CartItem。这三张表基本决定了商城系统能不能跑通。下面是一段适配 Django 的模型示例字段名以项目实际为准但思路完全一致from django.db import models from django.contrib.auth.models import User class Category(models.Model): name models.CharField(max_length50, verbose_name分类名称) parent models.ForeignKey(self, nullTrue, blankTrue, on_deletemodels.CASCADE, verbose_name父级分类) class Meta: verbose_name_plural 商品分类 def __str__(self): return self.name class Product(models.Model): name models.CharField(max_length200, verbose_name商品名称) category models.ForeignKey(Category, on_deletemodels.CASCADE, verbose_name所属分类) price models.DecimalField(max_digits10, decimal_places2, verbose_name单价) stock models.IntegerField(default0, verbose_name库存) image models.ImageField(upload_togoods/, blankTrue, verbose_name商品图片) created_at models.DateTimeField(auto_now_addTrue, verbose_name上架时间) class Meta: ordering [-created_at] def __str__(self): return self.name class CartItem(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name所属用户) product models.ForeignKey(Product, on_deletemodels.CASCADE, verbose_name商品) quantity models.PositiveIntegerField(default1, verbose_name数量) class Meta: unique_together (user, product)这段代码里的几个选型细节DecimalField而不是FloatField存价格是因为浮点数在购物车结算时会出现0.1 0.2 0.30000000000000004这类精度问题Decimal 能把金额精确到分。on_deletemodels.CASCADE的含义是删掉一个分类该分类下所有商品一并删除。这在期末演示时很方便但如果做真实商城更稳妥的是on_deletemodels.SET_NULL配合nullTrue避免误删分类导致商品全灭。unique_together保证同一个用户对同一件商品只能有一条购物车记录加购操作只需要在已有记录上增加quantity字段而不是插入大量重复行。ordering [-created_at]让商品列表默认按上架时间倒序前端模板里直接{% for product in products %}拿到的就是新商品在前不需要额外的查询逻辑。3.2 外键关联与字段参数的实际影响外键用得好不好直接影响后台管理页面的体验。在 Django Admin 里如果商品表外键关联分类表默认显示的是Category.__str__()的返回值。所以def __str__(self)必须写不然后台下拉框里全是「Category object (1)」这类天书。还有一个容易被忽略的参数是verbose_name。它决定 Admin 后台表单页的字段标签是显示「商品名称」还是「name」。期末答辩时导师打开后台看到的是中文标签印象分会明显不一样。字段再往细看ImageField依赖Pillow库如果没有安装migrate 时会直接报错。项目源码里出现了ueditor.css和app.css说明可能集成了富文本编辑器或自定义样式这会在商品详情页用到。如果商品描述字段用的是TextField而不是UEditorField说明编辑器是通过前端 JS 初始化的和 Django 模型层无关。3.3 迁移与表结构落地makemigrations 和 migrate写完了 models.py接下来是把模型变成真实的数据表python manage.py makemigrations python manage.py migrate python manage.py createsuperusermakemigrations会检查 models.py 相对上次迁移文件的变化生成一个新的迁移文件在应用的 migration 目录下。它只生成操作记录不实际操作数据库。migrate才是把迁移文件里的操作真正应用到数据库。如果你的数据库里已经有一批测试数据执行迁移前建议先备份。createsuperuser创建的管理员账号是进入 Django Admin 后台的钥匙。商城系统的后台数据维护——添加商品、调整库存、查看订单——都在 Admin 完成。把模型注册到后台需要在应用的 admin.py 里写from django.contrib import admin from .models import Product, Category, CartItem admin.site.register(Product) admin.site.register(Category) admin.site.register(CartItem)这个注册操作是 Django 最吸引人的地方一个模型只要注册进 Admin自动获得增删改查全部页面。期末项目用这个写「商城管理端」功能几乎零额外代码。但要注意默认的 Admin 列表页不好看需要定制这个在第 4 章展开。4. 商品展示、购物车与订单的 Django 实现4.1 商品列表查询与分页逻辑前端首页的商品列表视图层常用的写法是函数视图配合Paginator分页。Django 的Paginator类设计得比较优雅它把「切页」这件事拆成了「总数统计」和「切片取数据」两步。以商品列表为例from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.shortcuts import render from .models import Product def product_list(request): products Product.objects.select_related(category).all() paginator Paginator(products, 12) # 每页 12 条 page request.GET.get(page, 1) try: page_products paginator.page(page) except PageNotAnInteger: page_products paginator.page(1) except EmptyPage: page_products paginator.page(paginator.num_pages) return render(request, goods/list.html, {page_products: page_products})select_related是这里的关键。商品表外键关联分类表如果没有它模板里每次访问product.category.name都会额外发一条 SQL 查询。select_related会在查询外套一层JOIN把关联数据一次性取回来。100 个商品时两种写法差别不大但数据量到 1000 以上响应时间会肉眼可见地拉开差距。4.2 模板层的页面渲染与反向解析模板文件里商品列表的循环和 URL 跳转是标配写法{% for product in page_products %} div classcol-md-3 div classthumbnail img src{{ product.image.url }} alt{{ product.name }} div classcaption h4{{ product.name }}/h4 p classtext-danger¥{{ product.price }}/p a href{% url goods:detail product.id %} classbtn btn-primary查看详情/a a href{% url cart:add product.id %} classbtn btn-default加入购物车/a /div /div /div {% empty %} p暂无商品/p {% endfor %}模板里的{% url %}标签对应 urls.py 中的name参数# goods/urls.py app_name goods urlpatterns [ path(, views.product_list, namelist), path(detail/int:pk/, views.product_detail, namedetail), ] # cart/urls.py app_name cart urlpatterns [ path(add/int:product_id/, views.add_to_cart, nameadd), ]app_name加name的反向解析方案是 Django 官方推荐的写法。它的价值在于模板和视图里没有硬编码 URL后期调整路径结构比如把detail/int:pk改成goods/int:pk所有引用自动同步不会出现改了一处链接、全站 404 的情况。4.3 购物车的增删改查get_or_create 与 delete 的边界加入购物车的视图函数核心逻辑是「有记录就加数量没记录就新建」。Django 提供了get_or_create方法from django.shortcuts import redirect from django.contrib.auth.decorators import login_required from .models import CartItem from goods.models import Product login_required def add_to_cart(request, product_id): product Product.objects.get(pkproduct_id) cart_item, created CartItem.objects.get_or_create( userrequest.user, productproduct, defaults{quantity: 1} ) if not created: cart_item.quantity 1 cart_item.save() return redirect(cart:view)get_or_create的执行逻辑是一条 SELECT 查询查不到再 INSERT。带defaults参数时如果记录已存在Django 不会用 defaults 覆盖已有字段而是直接返回现有对象。这是一个容易踩的坑如果你的本意是「每次加 1」但把quantity1写进了defaults那么已有记录的数量会被重置为 1 而不是加 1。移除购物车商品用的是删除操作login_required def remove_from_cart(request, cart_item_id): CartItem.objects.filter( idcart_item_id, userrequest.user ).delete() return redirect(cart:view)这段代码特意在过滤条件里加了userrequest.user。它的意义在于数据隔离即便用户手动构造 URL 访问别人的购物车记录 ID删除操作也不会生效。期末项目可能没人攻击但养成这个习惯到真实项目里能避免越权漏洞。filter().delete()和get().delete()的区别是前者即使查不到也不会报错后者会抛DoesNotExist异常。操作类的删除用filter()更稳妥。4.4 Django Admin 后台定制把管理端做到能看默认的 Admin 后台列表页显示的是每条数据的__str__返回值字段少、没有筛选答辩演示时很没说服力。稍微定制一下效果立刻不同from django.contrib import admin from .models import Product admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display [id, name, category, price, stock, created_at] list_filter [category, created_at] search_fields [name, category__name] list_per_page 20 list_editable [price, stock]list_display控制列表页显示哪些字段list_filter在右侧生成基于分类和时间筛选的控件search_fields启用搜索框list_editable允许在列表页直接改价格和库存。注意list_display里可以放外键字段category但list_editable的字段必须同时出现在list_display里否则 Django 会抛出ValueError这也是常见的调试现场。Admin 界面的整体品牌信息也可以改admin.site.site_header 商城管理系统 admin.site.site_title 商城后台 admin.site.index_title 商品与订单管理这三行使后台页面标题、导航栏、首页标题全部中文化。导师打开后台看到的是「商城管理系统」而不是「Django administration」细节分就拉开了。5. 上线部署与排错collectstatic、MySQL 驱动与新手必踩的坑5.1 本地运行完整流程与验证方法拿到源码后按下面的顺序把项目跑起来成功率最高。不要跳过任何一步# 1. 进入项目根目录创建虚拟环境 python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate # 2. 安装依赖项目根目录通常有 requirements.txt pip install -r requirements.txt # 3. 检查数据库配置确保 DATABASES 指向正确的库 # 4. 生成数据表如果项目自带数据库文件这步之前先看数据库文件格式 python manage.py makemigrations python manage.py migrate # 5. 创建管理员账号 python manage.py createsuperuser # 6. 启动开发服务器 python manage.py runserver 0.0.0.0:8000启动后访问http://127.0.0.1:8000。验证要点如下验证项操作预期结果前台页面打开首页商品列表正常展示图片不裂图用户注册注册一个普通账号写入auth_user表无报错后台登录http://127.0.0.1:8000/admin用 superuser 登录后能看到商品和订单管理购物车点击「加入购物车」跳转到购物车页面数量累加正确下单结算提交订单订单表新增记录库存相应减少5.2 高频报错与修复对照表期末项目的排错80% 集中在下面几个区域按表索骥基本能解决报错信息根因修复方法ModuleNotFoundError: No module named mysqlclientMySQL 驱动缺失pip install mysqlclientWindows 装不上就下 whl 本地安装或改用 pymysqlOperationalError: (1045, Access denied for user...)MySQL 密码错误或用户权限不足检查 settings.py 中的 USER 和 PASSWORDdjango.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is requiredPython 版本内置的 SQLite 太旧升级 Python 到 3.8或切换到 MySQLstatic files 404开发模式未配置静态文件路径确认 settings.py 中STATIC_URL /static/模板中用了{% load static %}TemplateDoesNotExist模板文件路径配置问题检查 settings.py 的TEMPLATES配置中DIRS是否指向模板目录errno: 13 Permission denied静态文件目录没有写权限这通常出现在collectstatic阶段生产环境部署时改目录权限即可如果项目自带的是.sqlite3数据库文件启动前先确认 Django 版本和你本机版本是否兼容。SQLite 文件本身向下兼容性不错但 Django 版本差异可能导致迁移记录不一致。遇到Migration applied和代码模型对不上的情况备份数据后删掉django_migrations表再重新migrate是常见做法。还有一处容易被忽略的是django.contrib.admin的登录页样式。如果后台登录后页面没有任何 CSS检查 settings.py 里是否启用了django.contrib.staticfiles并且在 urls.py 的开发环境配置中添加了静态文件服务from django.conf import settings from django.conf.urls.static import static urlpatterns [ # 你的 URL 配置 ] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)上面的MEDIA_URL和MEDIA_ROOT对应商品图片的上传目录。ImageField的upload_togoods/保存的文件就落在 MEDIA_ROOT 下模板里{{ product.image.url }}输出的路径就是MEDIA_URL goods/xxx.jpg。开发模式下不配这段代码商品图片必然裂图。5.3 期末答辩的演示加分技巧这个项目既然拿过 97 分演示节奏是有讲究的。第一个演示点是「后台管理效率」用search_fields快速定位商品用list_editable直接把库存改成 0然后回到前台刷新页面商品详情页显示「已售罄」这个联动效果比背概念有说服力得多。第二个演示点是「数据表设计答辩话术」当导师问「购物车为什么不用 Session 而用数据库表」时答「Session 过期数据就丢数据库表能持久化而且订单生成后可以直接从购物车表取值不用做 Session 到表的转换」。如果项目用的确实是 Session则话术改为「Session 存储能减轻数据库压力适合期末演示场景」。第三个技巧是展示订单状态流。打开 admin 后台的订单列表说明状态字段用了choices选项然后现场把订单状态从「待发货」改为「已发货」。这些操作全部在 Admin 页面完成不需要写一行代码但体现的是你对 Django ORM 字段约束和后台定制能力的理解。本文还有配套的精品资源点击获取