ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Django+MySQL商城毕设源码:从环境搭建到答辩全流程拆解

Django+MySQL商城毕设源码:从环境搭建到答辩全流程拆解 简介这是一份基于Python的购物商城管理系统完整毕业设计项目包含可运行源码与配套数据库专为计算机相关专业毕业生准备也适合课程设计、期末大作业或有Python基础的实战练习者使用。压缩包共384个文件、约15.8MB核心由94个.py源码、59个.html页面及配套CSS/JS/XML配置组成另含3份.sql数据库脚本便于建表141张JPG与33张PNG图片用作商品及界面素材还附有“蔬果优选”项目开发过程与架构概述docx文档文件按模块归置结构清楚便于快速定位。该项目评审分98分源码经本地编译调试可运行商品展示、购物车、订单处理等电商关键模块均有完整代码支撑数据库脚本可省去手工建表步骤直接作为毕业设计蓝本或二次开发基础。目前已有239人学习下载是一套实用价值较高的Python商城学习资源。1. 一份Python商城毕设源码打开之后你到底拿到了什么搜到「基于Python的购物商城管理系统源码数据库毕业设计.zip」这个标题的人多数是两种处境一种已经把压缩包解压面对manage.py、models.py和几个.sql文件不知道下一步点哪里另一种是刚拿到毕设题目想找一条能走到答辩的稳妥路线。标题本身说明白了交付物——一套Python写的商城前后台源码、一份能恢复的数据库、以及安装运行的方法。这里面的关键点不在“代码能不能抄”而在你能不能在本机把服务跑起来、把数据导进去、把用户侧和管理侧两条链路讲清楚。这篇笔记按这个顺序拆先定技术栈再建数据库然后走核心业务代码最后把最容易翻车的地方和答辩前的验收路径给你摆出来。2. 先把技术栈拆清楚Django MySQL的组合为什么是毕设默认答案在这套毕设源码里Python是语言而商城Web服务需要一个Web框架。常看到的组合是Django MySQL少数用Flask SQLite。MySQL几乎是这类毕业设计事实上的标准数据库因为题目里写了“数据库”两个字老师在答辩时第一句往往就是“你的数据放在哪里、怎么保证一致性”。Django在这条路上的优势不只是写起来快更是它把商城管理系统里最重的“管理后台”部分替你完成了一半。2.1 Django Admin、ORM和认证商城里的“管理系统”一半是白送的先看“管理系统”四个字落在哪。一个购物商城管理系统用户侧是注册登录、商品浏览、加购下单管理侧是商品上架下架、订单状态修改、会员信息查看。Django自带admin后台只要把模型注册进去增删改查界面就直接能用。我一般会这样把Product注册进Adminfrom django.contrib import admin from .models import Category, Product admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display (id, name, category, price, stock, sales, is_on_sale) search_fields (name,) list_filter (is_on_sale, category) list_editable (price, stock, is_on_sale) list_per_page 20这段配置的效果是登录/admin/后商品列表直接显示id、名称、分类、价格、库存、销量、是否上架右上角搜索框对商品名做模糊查找左侧筛选器按上架状态和分类过滤右侧列表页能直接改价格、库存和上架开关不用点进编辑页。对毕业设计答辩来说这四样东西恰好覆盖了“商品管理”最常见的演示动作。list_display、search_fields、list_filter是ModelAdmin最常用的三个属性字段名必须与模型字段完全一致否则会报FieldErrorlist_editable里的字段不能同时出现在list_display之外否则会冲突。ORM解决的是“数据库增删改查”怎么落到代码里的问题。不需要手写SQLProduct.objects.filter(is_on_saleTrue)就是对shop_product表做条件查询Product.objects.create(...)是插入。以后老师问起“数据库这一块怎么设计的”你直接说“模型在models.py里数据库表由migrate生成”比背SQL语句要清晰得多。认证系统也自带django.contrib.auth提供User模型、authenticate()、login()、logout()三个核心函数注册、登录、会话保持的底层逻辑不需要自己造轮子。如果用Flask这些全部要自己拼Flask-SQLAlchemy管理ORM、Flask-Login管理会话、Flask-Admin做管理后台每一个都要额外配。灵活性是高但对一个要在几周内出成果的毕设来说Django把默认事情做完、把自由留给业务代码是更稳妥的选择。管理后台这一层光靠Django Admin就能演示掉“商品增删改查、订单状态修改”两块功能。2.2 项目结构与依赖文件clone下来第一件事是建虚拟环境装python依赖拿到源码压缩包别急着运行。我见过太多人直接在全局环境里执行pip install结果全家桶版本冲突最后连django都起不来。正确做法是每一个项目一套虚拟环境。先确认python安装没问题再创建虚拟环境python --version # 建议 Python 3.8 以上Django 4.x 才能跑 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate pip install -r requirements.txtvenv会在项目目录下生成一个独立Python环境pip install的包只对这个项目生效。激活后命令行前缀会出现(venv)这时再装依赖就不会污染系统Python。requirements.txt是这套源码依赖的清单一般长这样Django4.2.7 PyMySQL1.1.0 Pillow10.1.0Django是Web框架PyMySQL是让Python连上MySQL的驱动Pillow是处理商品图片上传必需的图像库。有些源码写的是mysqlclient而不是PyMySQLmysqlclient在Windows上经常编译失败踩过的人不少如果环境装不上我的习惯是改成PyMySQL并在项目包入口做一次适配这点在下一小节展开。装完依赖后用pip list确认django已经在虚拟环境里。注意如果 pip 下载依赖太慢可以在 install 命令后加国内PyPI镜像参数临时加速装完不影响项目本身。然后启动开发服务器python manage.py runserver 0.0.0.0:80000.0.0.0:8000表示监听所有网卡这样能在局域网里用同一WiFi下的手机访问页面答辩现场演示比只开127.0.0.1方便。能起服务只代表代码没语法错真正决定能不能跑通的是settings.py配置下面说三处必须过的。2.3 settings.py必改的三处数据库连接、媒体路径与密钥settings.py是整套源码的配置文件。第一处是DATABASES决定Django连哪个数据库。拿到源码后里面可能是SQLite的默认配置也可能是别人机器的MySQL账号密码都需要改成你自己的DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: shop_db, USER: root, PASSWORD: 你的mysql密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }NAME是数据库名必须先在MySQL里建好。HOST和PORT一般保持127.0.0.1和3306如果你的MySQL不是在默认端口这里就要对上。OPTIONS里的charsetutf8mb4很关键它保证中文正常读写不然商品名和订单地址会变成乱码。第二处是媒体文件配置。商品封面上传后Django把文件放在MEDIA_ROOT目录通过MEDIA_URL路径访问MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)如果不配最直观的翻车现象是首页商品图全部裂开、后台也看不到缩略图。第三处是SECRET_KEY。源码包里一般带了一个开发用密钥能跑但答辩前最好换掉尤其如果这个包是网上多人下载的公开源码。生成办法是重启一个Django项目从里面复制或者直接用secrets.token_hex(50)产出。这事不算必须但属于最基本的收尾意识。如果用的是PyMySQL替代mysqlclient一定要在settings.py同级目录的__init__.py里加这两行import pymysql pymysql.install_as_MySQLdb()不加这个Django在连MySQL时会报“ModuleNotFoundError: No module named MySQLdb”。install_as_MySQLdb()的作用是把PyMySQL伪装成MySQLdb接口Django的mysql后端就能正常走通。这样配置完服务能起来、库能连上下一步就是把数据库表结构铺出来。3. 数据库设计与初始化六张表、一次migrate、一份必带演示数据数据库设计是答辩时老师看得最仔细的部分。商城系统的最小闭环围绕“用户、商品、购物车、订单”四个对象展开。加上分类与订单明细一共六张核心表就够支撑整个演示流程。表结构设计得干净后面写业务代码几乎不用回头改。3.1 六张核心表的字段设计从商品分类到订单明细常见做法是每张表对应一个Django模型模型定义在models.py数据库表由迁移命令生成。先看表级设计表名作用关键字段shop_category商品分类id, name, parent_id, sort_ordershop_product商品id, category_id, name, cover, price, stock, sales, is_on_sale, created_atshop_cart购物车id, user_id, product_id, quantityshop_order订单主表id, order_no, user_id, total_amount, status, address, created_atshop_order_item订单明细id, order_id, product_id, product_name, price, quantity, subtotaluser_profile用户扩展信息id, user_id, phone, address商品分类表用自关联parent_id支持一级、二级分类毕设通常展示一级分类就够了留这个字段是为了演示时可以再加子类。商品表的price字段用DecimalField(max_digits10, decimal_places2)金额不能存Float浮点误差在订单结算时会被放大这是基础规范。字段模型代码就是直接描述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, on_deletemodels.CASCADE, nullTrue, blankTrue) sort_order models.IntegerField(default0) class Meta: db_table shop_category class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name所属分类) name models.CharField(max_length128, verbose_name商品名) cover models.ImageField(upload_togoods/, nullTrue, blankTrue, verbose_name封面图) price models.DecimalField(max_digits10, decimal_places2, verbose_name售价) stock models.IntegerField(default0, verbose_name库存) sales models.IntegerField(default0, verbose_name销量) is_on_sale models.BooleanField(defaultTrue, verbose_name是否上架) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: db_table shop_product ordering [-created_at]这里产品分类外键用了on_deletemodels.PROTECT意思是分类被商品引用时禁止删除分类防止商品变成无主孤魂。很多人会顺手写CASCADE结果删分类把商品也删了答辩演示时想恢复都难。这是一个细小但很能展示工程意识的点。订单主表与订单明细为什么要拆两张表一个订单可能有多个商品每个商品有自己的数量、单价和行小计。主表只存订单总额、状态、收货地址明细表存每一行。明细表里冗余了product_name、price快照这是刻意的——下单后商品改名、改价都不影响订单历史数据的准确性。老师问“为什么要冗余”这是个标准答案。订单状态我习惯用整数字段加choices来做class Order(models.Model): STATUS_CHOICES ( (1, 待付款), (2, 已付款), (3, 已发货), (4, 已完成), (5, 已取消), ) order_no models.CharField(max_length32, uniqueTrue, verbose_name订单号) user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name下单用户) total_amount models.DecimalField(max_digits10, decimal_places2, verbose_name订单总额) status models.IntegerField(choicesSTATUS_CHOICES, default1, verbose_name订单状态) address models.CharField(max_length255, verbose_name收货地址) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table shop_order状态机从1走到4管理后台直接改数字下拉框演示“发货”就是把状态从2改成3。order_no用时间戳加随机数生成保证唯一。3.2 从models.py到建表makemigrations的正确打开方式类定义好了数据库里还没有表。Django的迁移机制分两步makemigrations生成迁移脚本migrate执行建表。python manage.py makemigrations python manage.py migrate第一次跑会看到类似“Migrations for goods 0001_initial.py”的输出然后migrate把所有app的迁移都执行一遍包括Django自带的auth、admin、session表。这一步成功之后MySQL里执行SHOW TABLES;能看到django_开头的系统表和shop_开头的业务表。以后要修改表结构比如给Product加一个原价字段正确路径是改models.py → 再跑python manage.py makemigrations → 再跑migrate。不要直接在Navicat里改表改完Django不知道后面ORM一查询就报列不存在。MySQL数据库修改结构这件事在Django项目里应当由迁移命令统一管理这是源码能不能“换台机器也能跑”的根基。如果makemigrations提示No changes detected通常是app没有写进INSTALLED_APPS或者models.py根本没import。如果migrate报错提示表已经存在常见原因是别人把.sql文件和迁移混着用了。要么纯SQL导入要么纯migrate两条路不要混。3.3 初始化数据与后台账号没有一条在售商品的商城演示不了任何功能表建好是骨架还要灌数据。演示效果好不好很大程度取决于初始数据像不像真的。我会准备三个分类每个分类下5个左右商品价格有梯度、库存一部分为零来演示“缺货”。准备够12个商品是因为列表页要做分页演示每页通常显示12条数据太少分页按钮都出不来。两条初始数据路径一条是管理后台手工录入一条是fixture导入。手工录入用createsuperuser建管理员账号python manage.py createsuperuser按提示输入用户名、邮箱、密码。之后登录/admin在Category和Product页面手动添加。这种方式适合数据量小但商品图片准备起来麻烦。fixture方式是把数据导出成json换环境时一次性load进去python manage.py dumpdata goods.Category goods.Product --indent 2 shop_goods.json python manage.py loaddata shop_goods.jsondumpdata导出的文件里带的是模型记录不包含图片二进制图片文件本身还要复制到media目录。所以最稳妥的演示方式是先在本地手工把商品和图片配好导出json再把media目录一起打包换机器后loaddata加复制media两件事都做商品就齐全了。测试账号也可以dumpdata但那会带出密码哈希公开源码里不建议导出用户表只做演示环境内部用。4. 核心业务落地购物车、下单与库存扣减的完整代码路径数据库有了管理后台能编辑商品但用户侧的完整流程——浏览、搜索、加购、下单、扣库存——需要业务代码把它们串起来。这章写的是最简可跑的路径也是源码里最常被改动的地方。4.1 商品列表、分页与搜索关键词一页能拿得出手的ORM查询商品列表页是商城门面。逻辑是默认列出所有上架商品支持关键词搜索结果按12条一页分页。一个能直接放进views.py的写法from django.shortcuts import render from django.core.paginator import Paginator from django.db.models import Q from .models import Product def product_list(request): keyword request.GET.get(keyword, ).strip() qs Product.objects.filter(is_on_saleTrue) if keyword: qs qs.filter( Q(name__icontainskeyword) | Q(category__name__icontainskeyword) ) paginator Paginator(qs, 12) page_obj paginator.get_page(request.GET.get(page, 1)) return render(request, goods/list.html, { products: page_obj, keyword: keyword, })filter(is_on_saleTrue)只取上架商品下架商品自然不会出现在用户端。Q对象把两个搜索条件包成OR关系商品名或分类名任一命中就返还。icontains是大小写不敏感的包含匹配对应SQL里的LIKE %keyword%。Paginator(qs, 12)第一个参数是查询集第二个是每页条数get_page从URL的page参数取值访问/list/?page2就是第二页。keyword回传模板是为了搜索框里保留上次输入的关键词。模板里分页控件常见的坑是翻页时丢关键词。分页链接要写成?page{{ products.next_page_number }}keyword{{ keyword }}不然搜索后再翻第二页关键词没了结果变成全部商品。这个细节答辩演示时一翻页就露馅提前检查。4.2 购物车用session还是数据库表两种实现都要会购物车在毕设里有两派做法。匿名用户、临时用一下适合放session登录用户、跨设备保存适合放shop_cart表。很多源码会两者都实现未登录时购物车放在session里登录后把session购物车合并进数据库表。先看加购的session实现def add_to_cart(request, product_id): quantity int(request.POST.get(quantity, 1)) cart request.session.get(cart, {}) cart[str(product_id)] cart.get(str(product_id), 0) quantity request.session[cart] cart request.session.modified True return redirect(cart:detail)request.session是一个可写的字典对象key存商品idvalue存数量。因为product_id是整数字典key统一转成字符串避免类型错乱。request.session.modified True是告诉Django这个session被改过了必须保存不加这条部分情况下会话不会落盘。这个写法不需要登录就能演示加购答辩现场节奏很快未登录能加购比先登录再加购少一步。登录用户的购物车表实现from .models import Cart def add_to_cart_db(request, product_id): quantity int(request.POST.get(quantity, 1)) item, created Cart.objects.get_or_create( userrequest.user, product_idproduct_id, defaults{quantity: quantity}, ) if not created: item.quantity quantity item.save() return redirect(cart:detail)get_or_create按user和product两个条件查没找到就创建找到就累加。两次并发请求同时加购时数据库唯一约束可以兜底但get_or_create本身不是在高并发下绝对安全毕设场景足够用。登录合并session购物车到表的代码放在用户登录视图里def login_and_merge(request): # 正常登录逻辑成功后 cart request.session.get(cart, {}) for pid, num in cart.items(): item, created Cart.objects.get_or_create( userrequest.user, product_idint(pid), defaults{quantity: num}, ) if not created: item.quantity num item.save() request.session[cart] {} return redirect(index)场景session购物车数据库购物车未登录用户支持不支持跨设备同步不支持支持数据持久性浏览器会话结束清空永久保存答辩演示流程短、操作快更接近生产系统合并逻辑把session里的每个商品逐条写入数据库购物车然后清空session。这个细节答辩老师很爱追问题目基本是“未登录加购的商品登录后去哪了”能答出合并这条就算把业务闭环想全了。4.3 下单、库存扣减与事务边界防翻车的原子操作下单是整个系统里最容易翻车的环节因为涉及多张表生成订单、写入明细、扣商品库存、加商品销量、清空购物车。任何一步失败都不能留下半截订单。Django用transaction.atomic包住这一段任何异常整体回滚from django.db import transaction transaction.atomic def create_order(request): cart_items list(Cart.objects.filter(userrequest.user).select_related(product)) if not cart_items: raise OrderError(购物车为空) total sum(item.quantity * item.product.price for item in cart_items) order Order.objects.create( order_nogenerate_order_no(request.user.id), userrequest.user, total_amounttotal, status1, addressrequest.user.userprofile.address, ) for item in cart_items: product Product.objects.select_for_update().get(pkitem.product_id) if product.stock item.quantity: raise OrderError(f{product.name} 库存不足) product.stock - item.quantity product.sales item.quantity product.save() OrderItem.objects.create( orderorder, productproduct, product_nameproduct.name, priceproduct.price, quantityitem.quantity, subtotalitem.quantity * product.price, ) Cart.objects.filter(userrequest.user).delete() return order这个方法逐行说明。select_related(product)一次性把购物车关联的商品查出来避免循环里每行触发一次数据库查询数量少时感觉不明显商品多了就是几十倍的耗时差。total用Decimal参与sum运算得到的是Decimal对象不会像float那样积累误差。先创建Order主表拿到主键再循环写明细。select_for_update()对商品行加锁直到事务结束才释放两人同时抢最后一件商品时第二个请求会等第一个事务提交后才读到最新库存防止超卖。库存不足时raise异常事务整体回滚这个订单也会撤销不会留下“有订单没扣库存”的脏数据。提示select_for_update 在 MySQL 的 InnoDB 引擎下有效SQLite 对行锁支持有限毕设演示事务用 MySQL 才有说服力。订单号生成函数随手写一个工具方法import time import random def generate_order_no(user_id): return f{time.strftime(%Y%m%d%H%M%S)}{user_id:04d}{random.randint(100, 999)}时间戳精确到秒加用户ID加三位随机数并发演示时重复概率很低。如果老师问你“并发下库存怎么保证不超卖”能说出select_for_update加事务回滚这道题就算过了。反过来如果代码里没有锁、没有事务下单逻辑各自save那是典型的扣库存翻车写法需要重点排查。5. 编不过、跑不起来、数据对不上毕业设计最常见的5个坑与排查源码下了好几份换了三台电脑最后卡在同一类报错上的情况我见得太多了。这一章把最常见的坑按现象、原因、解决三步写清楚按顺序排查能覆盖八成运行期报错。5.1 坑一No module named djangopip装了却还是找不到模块现象在项目目录执行python manage.py runserver立刻报ModuleNotFoundError: No module named django。但执行pip list却能看到django已经装好了。原因你pip install时用的是全局Python而运行命令时被虚拟环境隔离了或者根本没创建虚拟环境两个Python混用。最常见的情形是Windows上装了多个Python版本命令行默认的是3.7而包装进了3.11那一套。解决先where pythonWindows或which pythonmacOS/Linux看当前Python路径。确认激活了本项目虚拟环境再执行pip install -r requirements.txt。激活后命令行前缀出现(venv)再跑which python路径指向venv目录下的python。装完用pip list确认django在那一个环境里。如果实在分不清直接把虚拟环境删掉重建rm -rf venv然后重新python -m venv venv。5.2 坑二MySQL连不上报2003 Cant connect现象执行migrate或者runserver后首次访问数据库时报django.db.utils.OperationalError: (2003, Cant connect to MySQL server on 127.0.0.1)。原因最直接的是MySQL服务没启动。其次是DATABASES里的HOST、PORT、密码和本机MySQL对不上。还有一种是MySQL从8.0开始默认认证插件是caching_sha2_password而某些PyMySQL老版本不兼容。解决先确认MySQL服务在跑——Windows在服务管理器里看MySQL80是否启动macOS执行brew services list。命令行用mysql -u root -p -h 127.0.0.1 -P 3306试连连不上就是服务或端口问题。账号密码在settings.py里改成能登录的那组。认证插件问题则升级PyMySQL到1.1.0以上并确认OPTIONS里charset是utf8mb4。5.3 坑三建表后中文全是乱码现象管理后台录入商品名保存再刷新页面上全是????或者“锟斤拷”。原因MySQL数据库或表的字符集不是utf8mb4Django写入的UTF-8数据被按latin1解释。建库时默认字符集继承自服务器配置如果服务器默认是latin1那库表就跟着错了。解决建库时明确指定字符集CREATE DATABASE shop_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;已经建错库的可以改库默认字符集再转换表ALTER DATABASE shop_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE shop_product CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;改完库表还不够settings.py连接里OPTIONS的charsetutf8mb4也要配上。两者对齐后把乱码数据删掉重新录入旧乱码数据不会自动修复。这个坑在答辩前一天晚上爆出来的概率非常高提前用SHOW CREATE TABLE shop_product确认一下最省心。5.4 坑四商品图片全部裂掉现象后台能上传图片但前台商品图、后台缩略图显示为一个broken image图标。原因MEDIA_URL配置了但开发服务器没有挂载媒体文件路由。Django的runserver默认不处理media目录需要手工在urls.py挂上。解决在项目的urls.py末尾加from django.conf import settings from django.conf.urls.static import static if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这段只在DEBUGTrue时生效生产部署用nginx处理media目录与Django无关。加了之后重启runserver图片路径/media/goods/xxx.jpg应该能直接访问。如果还是裂检查MEDIA_ROOT指向的目录是否存在、图片文件是否真的在Django不会自动创建目录目录不存在时上传会报错。5.5 坑五目录里有db.sqlite3启动后却提示连了MySQL现象源码压缩包里带着一个db.sqlite3文件以为改一下就能演示结果启动后Django报连不上MySQL或者页面数据为空。原因settings.py的DATABASES配的是mysql后端Django根本不读sqlite3文件。也有相反情况配置没改、默认连了SQLite但题目要求是MySQL交上去的说明里数据库对不上。解决先明确这份源码要交给老师的运行环境是什么。以MySQL为准的话把db.sqlite3当作历史遗留文件忽略按坑二的步骤把MySQL链路跑通再migrate建表导入初始数据。把sqlite当作临时数据源时可以用Django的databases路由把旧sqlite里的数据导出来但毕设演示一般不值得花这个时间直接重新初始化更快。重点是确保交付文档里写清楚数据库在MySQL里重建流程是migrate加loaddata。6. 答辩前的自查清单三条主链路演示完这篇毕设就稳了到答辩前功能点不再加了把三条链路跑通就行。按下面这个顺序自测每条链路走完并截图留档现场演示就不容易卡壳。6.1 主链路一用户侧从注册到支付开两个浏览器窗口一个普通用户、一个管理员。用户侧注册新账号 → 登录 → 搜索关键词 → 商品列表翻页 → 进详情 → 加购 → 购物车页改数量 → 提交订单 → 模拟支付本地项目一般是把订单状态从1改成2。每一步验证页面跳转不报错、数据写入正确。6.2 主链路二管理侧从商品到订单管理员登录/admin → 新增一个商品分类 → 在分类下新增商品上传一张本地图片→ 到前台确认新商品出现在列表里 → 给用户订单改状态为已发货 → 用户侧看到物流状态变化。这条链路验证的是Django Admin与业务数据的联动也是老师最容易操作的部分。6.3 主链路三数据一致性自查查三组数据对不对得上订单总额 明细行小计之和下单前后商品库存减少量与订单购数量一致商品销量与各订单明细数量累加一致。可以用几条SQL快速核SELECT o.id, o.total_amount, SUM(i.subtotal) AS item_sum FROM shop_order o LEFT JOIN shop_order_item i ON o.id i.order_id GROUP BY o.id, o.total_amount HAVING o.total_amount ! item_sum;查询结果为空说明金额对得上。把这三条链路的操作步骤整理成一页纸的checklist配合截图放进说明书附录答辩现场照着走比临时翻代码稳得多。我自己带毕设时最深的教训是所有时间花在写代码上却没花时间把验收路径过一遍结果现场演示时在“注册后没自动登录”这种小细节上卡了半分钟而那半分钟足够让老师对整份工作的印象打折扣。先别加新功能把主链路测通、把坑填平、把讲词顺一遍希望帮到你。本文还有配套的精品资源点击获取
返回列表