尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Django花卉商城系统毕业设计:从数据库建模到部署答辩全攻略

Django花卉商城系统毕业设计:从数据库建模到部署答辩全攻略 毕业设计季最常被问到的一个问题就是学长Django项目有什么好题目推荐吗要那种能跑通、能演示、能答辩的。问得多了之后我发现大家真正要的不是一个看起来高大上的课题名称而是一个功能边界清晰、技术栈成熟、能在一两个月内真正做完做透、并且答辩时经得起追问的项目。基于Django的花卉商城系统其实就是这个定位下非常典型的一类选择。它把电商系统最核心的几条链路——商品展示、用户体系、购物车、订单流转、后台管理——全部囊括进去既不过度复杂到学生做不完也不简陋到显得没技术含量。这篇文章我就把整套系统的设计和实现思路完整拆开讲一遍从选题理由、技术选型、数据库建模到核心功能实现、后台定制、安全部署再到最后答辩时的应对思路全部按我实际带项目的经验来写。如果你是正在选毕设题目的学生或者已经选了类似商城系统但不知道怎么下手的这篇文章值得你从头看到尾。1. 为什么选花卉商城做毕设需求边界与功能定位1.1 选题背后的考量电商类项目的答辩友好度电商系统是计算机专业毕业设计里出现频率极高的一类题目。原因很直接电商业务链路清晰从商品到订单到支付每一步都能找到对应的技术点而且用户对电商系统的使用习惯非常熟悉演示的时候不需要花时间解释业务逻辑评委老师一眼就能看懂你在做什么。花卉商城相比通用电商平台更有辨识度。花卉商品有几个特点一是图片展示要求高适合体现图片上传、缩略图处理、轮播展示这类功能二是花卉有分类学属性玫瑰、多肉、绿植、盆栽等天然适合做分类管理和筛选三是花卉消费具有节日属性情人节、母亲节、开业庆典可以做营销相关的扩展功能比如促销活动、优惠券等。这些特性让整个项目在演示时容易讲出故事感。从工作量控制角度看花卉商城的核心模块控制在5到8个比较合理用户注册登录、商品浏览搜索、购物车管理、订单生成与状态管理、后台商品与订单管理、数据统计。再往上加支付对接、物流跟踪、消息通知就会明显增加难度对毕设来说容易收不住尾。1.2 角色划分与核心业务链路谁来买、谁来管、怎么赚钱整个系统的角色划分直接决定数据库设计和权限控制的复杂度。最简模型是两类角色普通用户前台消费者和管理员后台运营者。如果你希望增加一点差异化可以加入商家角色让商家可以上架自己的花卉商品管理员负责审核但这会把整个权限体系复杂化不少。我这里按最常见的双角色方案说明业务链路。用户端的核心链路是注册登录 → 浏览商品 → 搜索/筛选 → 加入购物车 → 生成订单 → 模拟支付 → 查看订单状态。管理端的核心链路是登录后台 → 管理商品分类 → 管理商品库存 → 处理订单状态 → 查看销售统计。这里有一个关键设计取舍支付环节。真实对接支付宝或微信支付需要企业资质且涉及到回调验证、签名等逻辑对毕设来说成本过高。多数课程设计采用模拟支付方式——在订单页生成虚拟订单点击确认支付按钮后直接跳转到支付成功页面订单状态变为已支付。这样做既保留了完整业务链路又规避了支付接口申请的现实门槛。答辩时只要大方说明这是模拟支付环节真实场景会用支付平台的沙箱环境完全站得住脚。1.3 功能清单如何恰到好处不多不少正好满足学分要求很多学生容易陷入一个误区功能越多越好把自己能想到的模块全部堆进去。结果往往是开发周期拉长代码质量和文档质量双双下滑答辩时反而被细节问题问倒。我的建议是采用核心功能必做 扩展功能按时间选做的策略。核心功能包括用户模块注册、登录、退出、个人信息修改商品模块商品列表、商品详情、分类筛选、关键词搜索购物车模块加入购物车、修改数量、删除商品、批量结算订单模块提交订单、订单列表、订单详情、状态流转待支付/已支付/已发货/已完成/已取消管理后台商品管理增删改查、分类管理、订单管理发货/取消、用户管理扩展功能包括轮播图管理、公告管理、销售数据统计图表、商品评论、收藏、密码重置邮件发送、分页优化、搜索历史记录等。选一个扩展功能认真做透比做五个粗糙的功能有价值得多。比如一个带有日期筛选和图表展示的订单统计页技术上涉及聚合查询、ORM分组、前端图表库接入三个知识点答辩时可以从这三个角度展开讲比罗列一堆半成品功能更有说服力。2. Django技术栈选型为什么是这套组合而不是其他2.1 框架版本的现实选择Django 3.x还是4.xDjango版本选择是个很现实的问题。目前主流教学资源和网上的开源项目多基于Django 2.x、3.x、4.x新版Django 5.x也已经发布。对于毕设来说我推荐优先选择Django 3.2 LTS或4.2 LTS理由有三点。第一点是兼容性。很多教学资源、第三方库、博客教程都是围绕这几个版本写的遇到问题搜索时能直接找到解决方案。第二点是Python版本要求Django 3.2支持Python 3.6到3.10Django 4.2支持Python 3.8到3.12绝大多数学校机房和学生的本机环境都能满足。第三点是LTS版本有长期维护保障期间的安全更新和bug修复都很稳定不会突然遇到兼容性大坑。Python版本尽量选择3.8到3.11之间的版本。不要在Python 3.12上跑旧版Django容易遇到第三方库比如处理图片的Pillow的兼容问题。个人实测最稳定的组合是Python 3.9 Django 3.2或者是Python 3.10 Django 4.2。2.2 模型、视图、模板三板斧如何撑起整个商城Django的MTV架构Model-Template-View对商城系统来说非常契合。M层负责数据建模和业务逻辑T层负责页面渲染V层负责接收请求返回响应。初学者最容易犯的错误是试图在所有地方用类视图Class-Based ViewsCBV或者反过来全部用函数视图Function ViewsFBV。我的实践建议是核心业务逻辑用函数视图因为思路直观、调试方便列表展示类页面用类视图ListView因为分页和模板渲染的样板代码少很多。比如商品列表页适合用ListView配合Paginator分页而加入购物车、提交订单这种涉及多步校验的逻辑用函数视图逐行写清楚每一步会更不容易出错。模板层的设计也有一些原则。一个基础模板base.html存放公共的头部、导航栏、底部和CSS/JS引用子模板通过继承和block替换内容区域。前端框架用Bootstrap比较省心版本选Bootstrap 4或5都行5代更现代一点4代的兼容性更好教程也多。如果对页面颜值有更高要求可以套一个免费的AdminLTE后台模板用于管理端前台则自己改造Bootstrap样式这套组合下来页面观感基本能达到能拿得出手的水平。2.3 上传图片与静态文件的本地化处理思路花卉商城的所有商品都依赖图片展示所以media文件的处理是个绕不开的点。Django处理上传图片的基础是设置两个配置项MEDIA_URL和MEDIA_ROOT同时需要依赖Pillow库来处理ImageField。很多初学者在这里踩坑开发环境下图片上传成功了但页面死活显示不出来。原因在于没有在项目URL中添加media文件的serving配置。开发环境下需要这样配置# config/urls.py项目主路由 from django.conf import settings from django.conf.urls.static import static urlpatterns [ # ... 其他路由 ] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这个配置只在DEBUG模式下生效因为生产环境应该由Nginx这类Web服务器来托管静态文件和media文件。毕设演示基本都在本地跑加上这个配置就能解决90%的图片显示问题。商品图片还可以做一点优化在商品列表中展示时用缩略图而不是原图因为原图动辄几百KB到几MB列表页一次性加载几十个商品会造成明显卡顿。Pillow库可以在保存图片时生成固定尺寸的缩略图或者前端用CSS控制图片显示尺寸后者更简单。如果前后端都想省事可以在模板中使用Django的模板标签将图片路径传给前端由前端CSS设置统一宽高并加object-fit: cover样式这样既美观又不需要额外的处理代码。3. 数据库模型设计花卉、订单和用户之间的关联是成败关键3.1 用户模型继承AbstractUser而不是另起炉灶用户模块是商城系统的第一块地基。Django内置的User模型自带了用户名、密码、邮箱、姓名、手机号等字段但商城系统中通常还需要额外的信息比如收货地址、头像、注册时间等。正确的做法是继承AbstractUser扩展而不是自己从零建一张用户表。继承AbstractUser的核心写法from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): nickname models.CharField(max_length50, verbose_name昵称, blankTrue) phone models.CharField(max_length11, verbose_name手机号, blankTrue) avatar models.ImageField(upload_toavatars/, verbose_name头像, blankTrue, nullTrue) address models.CharField(max_length200, verbose_name收货地址, blankTrue) class Meta: db_table shop_user verbose_name 用户 verbose_name_plural verbose_name同时必须在settings.py里告诉Django使用自定义用户模型AUTH_USER_MODEL users.User这一步必须在第一次迁移之前设置好。如果已经先执行了migrate再回头改用户模型会遇到外键关联断裂的麻烦那时候要么删库重建要么做复杂的数据迁移非常痛苦。所以项目刚创建时第一件事就是建好App和自定义User再跑首次迁移。与用户关联的收货地址表建议独立一张表因为一个用户可能有多个常用地址。用ForeignKey关联User即可on_deletemodels.CASCADE表示用户删除时地址一起删除这在商城场景中是合理的。3.2 商品、分类、库存的多表关联设计商品模块是花卉商城展示层的基础模型设计至少要包含分类、商品、商品图片三张表。分类表可以设计无限级分类父分类外键指向自身但花卉商城商品量不大二级分类已经绰绰有余。一级分类放观花植物、观叶植物、多肉植物、盆景绿植二级分类放具体品种比如玫瑰、百合、绣球等。商品的模型设计要重点考虑信息的完整性我的推荐字段如下class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name商品分类) name models.CharField(max_length200, verbose_name商品名称) price models.DecimalField(max_digits10, decimal_places2, verbose_name销售价格) original_price models.DecimalField(max_digits10, decimal_places2, verbose_name原价, blankTrue, nullTrue) stock models.PositiveIntegerField(default0, verbose_name库存) sales models.PositiveIntegerField(default0, verbose_name销量) description models.TextField(verbose_name商品详情) is_on_sale models.BooleanField(defaultTrue, verbose_name是否上架) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: db_table shop_productprice用DecimalField而不是FloatField这个点必须提醒。FloatField在处理金额时会有二进制浮点误差比如0.10.2不等于0.3金额计算一旦出现分以下的误差就是事故。DecimalField声明decimal_places2所有计算交给Python的decimal模块处理安全可靠。category外键用on_deletemodels.PROTECT而不是CASCADE这个选择很关键。PROTECT意味着如果分类下还有商品删除分类时会抛出ProtectedError异常从而倒逼你先处理分类下的商品。这比CASCADE悄悄把商品也删了要安全得多演示和答辩时都可以正面讲这个设计。商品图片表建议和商品表拆分一个商品对应多张图主图表里存一个默认展示图图集表存其余图片。列表页只需要查询主图表详情页再查询图集避免每页加载大量图片数据。3.3 购物车与订单临时态与持久态怎么切换购物车是商城系统里最容易设计得过重或过轻的模块。常见方案是给每个用户建一张购物车表记录user_id、product_id、数量、选中状态。这个方案直观但有一个小问题未登录用户无法添加购物车。更精细的方案是在线购物车 临时购物车双轨制。在线购物车用数据库表存临时购物车用Session存用户登录后把Session中的购物车合并到数据库购物车。这个方案体验好但实现逻辑比较复杂涉及合并冲突处理、失效商品清理等细节对毕设来说可能过度设计。我的建议是先做数据库版购物车保证登录用户能正常加购下单。如果有余力再处理Session购物车。代码实现上购物车表就两个关键字段数量quantity和选中标识selected。选中标识的作用是支持部分结算用户勾选几件商品去结算没勾选的不结算符合电商系统常规交互。订单模块的设计则要考虑一次下单多件商品的场景。这里需要三张表订单主表Order一个订单、订单商品关联表OrderItem订单下的每件商品、以及订单状态的历史记录可选。OrderItem用ForeignKey指向Order同时用另一个ForeignKey指向Product。这里的问题是如果商品被删除了OrderItem里的商品外键就会悬空。正确处理方式是把商品名称、商品价格、图片路径等关键快照信息冗余存储到OrderItem字段里。这样即使用户下单后商品被修改价格或下架历史订单仍然能显示当时快照信息。这一点在真实电商系统中是标准做法答辩时提出来会加分。3.4 一对多删除策略为什么用PROTECT而不是CASCADE上面提到商品分类外键用PROTECT这里展开讲讲on_delete参数的实战选择。Django外键的on_delete主要有CASCADE、PROTECT、SET_NULL、SET_DEFAULT四种选择。CASCADE适合从表生命周期完全依赖主表的场景比如用户删除后他的收货地址也删除。但如果把CASCADE用在商品分类上就会出现删除一个分类把分类下几十个商品全部带走的严重后果这在运营中是不可接受的误操作。PROTECT则要求有引用关系时禁止删除主表记录更符合电商运营场景。比如某个分类还有商品你尝试删除分类时会收到错误提示必须先把分类下的商品转移或删除。对演示型毕设来说PROTECT反而能展示你对数据安全的设计思考。SET_NULL则适合弱引用场景比如用户删除后保留订单记录把订单里的用户外键置为NULL。订单表里不适合用CASCADE因为订单数据是交易凭证用户注销账号不应该销毁订单记录。这一点值得在设计时换掉很多教程里默认的CASCADE做法。4. 核心功能逐个拆解登录、购物车、下单、结算4.1 用户注册登录与验证码内置认证的扩展姿势Django自带的认证系统非常完善。user_login视图用内置的authenticate和login函数user_logout用logout函数几行代码就能实现完整的登录注销流程。注册功能需要额外处理表单验证用户名是否重复、两次密码是否一致、邮箱格式是否合法。可以用Django ModelForm配合自定义校验方法实现。需要注意一点明文密码绝对不能直接写入数据库Django的create_user方法会自动调用set_password对密码做哈希加密千万不要用create方法代替。图形验证码功能在毕设中频率很高。引入captcha库django-simple-captcha二十分钟就能搞定。注册页放一个验证码框点击刷新后端校验时把表单里的验证码和Session里存的验证码比对。这个功能实用性强在很多课程设计评分标准里属于加分项。如果不想引入额外依赖也可以输入答案在Session里做校验效果差不多只是防机器注册的能力弱一些。登录状态判断用模板层的user.is_authenticated即可。这里有个前端交互细节导航栏的登录/用户名按钮应该根据登录状态动态切换未登录显示登录/注册登录后显示用户名和退出按钮。用Django模板的{% if user.is_authenticated %}判断就可以实现不需要引入前端框架的状态管理。4.2 商品列表与详情页的查询优化避免N1查询商城首页和列表页看起来简单但性能问题容易在这里翻车。最典型的错误是在模板中循环商品时访问商品关联的对象产生N1查询问题页面加载一次查询每件商品又额外查询一次关联数据几十件商品就多出几十次数据库查询。正确做法是用Django ORM的select_related方法在查询时一次性JOIN出关联数据。商品列表的典型查询products Product.objects.filter(is_on_saleTrue).select_related(category)select_related适用于一对一和一对一外键查询它通过SQL JOIN一次查出关联对象避免循环中重复查询。对多对多或者一对多反向查询用prefetch_related它是先查主表再查关联表然后Python内存中拼接。商品搜索功能可以用Q对象实现支持多字段匹配from django.db.models import Q keyword request.GET.get(keyword, ) if keyword: products products.filter( Q(name__icontainskeyword) | Q(description__icontainskeyword) )这里用icontains而不是contains原因是不区分大小写中文搜索时没有影响但英文花卉名比如rose、lily搜索更友好。搜索列表支持按价格排序、按销量排序、按新品排序对应ORDER BY的三种条件切换实现成本很低但对功能的完整度提升很明显。4.3 购物车的会话级实现不登录也能加购的细节前面聊了数据库购物车方案这里再补充一个轻量实现Session购物车。如果你的需求明确是必须能演示未登录加购有些老师会关注这一点Session购物车是成本更低的方案。Session购物车的核心思想是把购物车数据直接存在Session中用商品ID作为键、数量作为值。加购时就更新Session字典不需要数据库表。def add_cart(request, product_id): product Product.objects.get(pkproduct_id) cart request.session.get(cart, {}) key str(product_id) cart[key] cart.get(key, 0) 1 request.session[cart] cart return JsonResponse({status: ok, cart_count: sum(cart.values())})这个方案的好处是零数据库表、代码量小适合小型毕设项目。但缺点也很明显Session数据是序列化存储的电商商品数量大时Session体积会膨胀且购物车数据无法跨设备同步。所以更成熟的方案是上一节说的数据库购物车 登录后合并Session。如果采用数据库版购物车界面交互会有两个关键细节。第一购物车列表里每件商品要有数量加减按钮点击后通过AJAX异步更新数据库数量同时刷新页面上的小计和总价这一步实现不难但很能体现细节完成度。第二全选/取消全选按钮以及单件勾选对应购物车表里的selected字段结算时只处理selectedTrue的商品这个逻辑要在视图函数里用filter标注清楚。4.4 订单创建与库存扣减事务的原子性为什么必须加订单创建是整个系统里最不能出错的地方因为它涉及多张表的数据变更创建订单主表、批量创建订单商品明细、扣减商品库存、清空购物车。任何一个环节失败都必须回滚全部操作否则会出现订单创建了但库存没扣或库存扣了但订单没建这种数据不一致问题。Django的transaction.atomic就是解决这个问题的标准工具from django.db import transaction transaction.atomic def create_order(request): # 1. 创建订单主记录 # 2. 遍历购物车选中商品创建订单明细 # 3. 扣减库存Product.objects.filter(pkid, stock__gtequantity).update(stockF(stock) - quantity) # 4. 清空购物车 pass这里有一个高级技巧扣库存时用filter(stock__gtequantity) F表达式 update而不是先查后改。原因有二。第一先查询再更新存在并发风险两个用户同时购买最后一件商品时可能超卖第二update F是在数据库层面原子执行的天然避免并发问题。虽然毕设演示不会真有并发流量但代码里体现出这个意识技术含量就上来了。F表达式的用法如下from django.db.models import F updated Product.objects.filter(pkproduct_id, stock__gtequantity).update(stockF(stock) - quantity) if updated 0: raise Exception(库存不足)update返回受影响的行数如果返回0说明库存不够或者商品不存在这时候抛出异常让事务回滚整个订单就不会创建成功。这套逻辑用很少的代码就保证了数据一致性。订单主表的订单号问题也值得注意建议不要用自增ID直接做订单号而是用时间戳随机数字生成类似20250508152300123456的字符串更符合真实系统的观感。5. 管理后台与数据可视化让评审老师眼前一亮的部分5.1 定制Django Admin列表页、筛选器、搜索的配置技巧Django自带的Admin后台是毕设项目里性价比极高的亮点。只需要注册模型并配置ModelAdmin就能得到一个成熟的增删改查后台完全不花心思。但直接用默认配置会显得有些敷衍我建议至少做四个方面的定制。第一列表页显示的字段默认只显示模型的__str__通过list_display配置多列展示admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display (name, category, price, stock, sales, is_on_sale, created_at) list_filter (category, is_on_sale) search_fields (name,)list_filter让右侧出现分类和上下架状态的筛选器search_fields让顶部搜索框能按商品名称搜索这些都是零成本的高完成度配置。第二编辑表单里图片字段直接显示预览需要自定义表单或者通过admin的formfield_overrides配置Pillow搭配ImageField之后默认就能看到上传组件如果再配置一下缩略图预览演示效果会更好。第三外键关联对象的展示默认是一个下拉选择框如果商品数量很多可以配置autocomplete_fields让选择框变成可搜索的自动补全。第四可以为操作增加按钮比如订单列表页增加发货操作通过定义一个自定义管理动作来实现。5.2 电商订单状态流转的简易实现订单状态是电商系统中最典型的流程控制场景。状态定义建议用整数字段而不是字符串因为数字便于比较和扩展。具体定义0待支付、1已支付、2已发货、3已完成、4已取消。状态流转在管理后台里用操作按钮实现。Django Admin的自定义操作可以通过actions属性添加也可以重写模型的方法。更精细的交互是给订单详情页添加一个表单管理员点击按钮提交后视图函数里校验当前状态是否允许流转到目标状态然后更新字段。校验逻辑封装成模型方法便于复用class Order(models.Model): STATUS_CHOICES [ (0, 待支付), (1, 已支付), (2, 已发货), (3, 已完成), (4, 已取消), ] status models.IntegerField(choicesSTATUS_CHOICES, default0, verbose_name订单状态) def transition_status(self, new_status): allowed { 0: [1, 4], 1: [2, 4], 2: [3], 3: [], 4: [], } if new_status not in allowed.get(self.status, []): raise ValueError(f状态不允许从{self.get_status_display()}流转到{self.get_new_status_display()}) self.status new_status self.save()这个方法的巧妙之处在于状态流转规则集中在一处任何地方调用都做同一套校验。前端页面根据订单状态渲染不同的操作按钮待支付显示取消订单已支付显示确认收货发货后再显示已发货显示确认收货已取消和已完成不再显示操作按钮。这套交互做出来之后订单模块的演示节奏会很顺。5.3 数据统计页的可视化方案ECharts图表怎么接数据可视化是商城里最容易出彩的部分技术复杂度其实不高。核心思路后端视图统计数据库数据返回JSON格式的数据前端引入ECharts图表库用AJAX请求获取数据渲染柱状图和饼图。统计维度建议三个近7天订单量趋势折线图分类商品销量占比饼图热门商品Top10横向柱状图。后端可以用Django ORM的annotate和Count做聚合from django.db.models.functions import TruncDate from django.db.models import Count, Sum daily_orders ( Order.objects .filter(created_at__gtestart_date) .annotate(dayTruncDate(created_at)) .values(day) .annotate(order_countCount(id)) .order_by(day) )这段代码的含义是过滤出最近7天的订单按天截断日期统计每天的订单数。TruncDate是Django 2.0之后原生支持的函数比用Python循环再分组要优雅得多。前端如何接数据是另一个重点。我在实际项目里最顺手的方案是后端写一个接口返回JSON前端用原生fetch或jQuery的ajax请求把数据塞给ECharts初始化逻辑。ECharts的Apache 2.0开源协议可以放心使用直接下载或者是用CDN引入都行。页面布局用管理后台里的一个独立页面顶部放四个统计卡片总销售额、今日订单数、总订单数、总商品数下面放图表整体观感比普通表格好非常多。需要注意一个实战细节ECharts的初始化必须在图表容器挂载到DOM之后执行否则画布宽度计算为0图表无法正常渲染。一个稳妥的做法是在window.onload或$(document).ready中初始化。6. 前后端交互的几种典型写法AJAX、表单提交与分页6.1 AJAX加入购物车的CSRF处理加入了购物车的无刷新交互后你会立刻遇到一个Django新手必踩的坑CSRF防护。Django默认开启CSRF中间件所有POST请求必须携带CSRF Token否则返回403。对于传统表单提交Django模板中含有{% csrf_token %}不需要额外处理。但AJAX请求并不会自动带上这个Token。使用jQuery时的标准做法是从cookie中读取csrftoken统一设置到header里function getCookie(name) { let cookieValue null; if (document.cookie document.cookie ! ) { const cookies document.cookie.split(;); for (let i 0; i cookies.length; i) { const cookie cookies[i].trim(); if (cookie.substring(0, name.length 1) (name )) { cookieValue decodeURIComponent(cookie.substring(name.length 1)); break; } } } return cookieValue; } $.ajaxSetup({ headers: { X-CSRFToken: getCookie(csrftoken) } });这段代码在初始化时运行一次之后所有AJAX的POST请求都自动带上了CSRF Token。这里有个容易被忽略的前提Django会为每个Session颁发一个csrftoken cookie前提是请求过程中使用到CSRF保护或者模板里渲染过{% csrf_token %}。如果页面上完全没有csrf相关的模板标签Cookie可能不存在。稳妥的解决方式是在模板的form标签里都加上{% csrf_token %}即使表单不需要POST也一样。6.2 分页组件的三种写法对比哪种最稳商品列表、搜索结果、订单列表都可能数据量很大分页是必做功能。Django分页有三种常见写法我按推荐程度排序。第一种是自定义paginator最通用、可控性最强。视图里处理逻辑from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger paginator Paginator(products, 9) page request.GET.get(page) try: current_products paginator.page(page) except PageNotAnInteger: current_products paginator.page(1) except EmptyPage: current_products paginator.page(paginator.num_pages)如果没有捕获EmptyPage访问超出范围的页码时会直接抛出404交互体验很差。捕获后返回最后一页更友好。第二种用ListView配合paginate_by参数代码量最少class ProductListView(ListView): model Product template_name shop/product_list.html context_object_name products paginate_by 9模板里自动就有page_obj变量可用。这个方案最大的优点是省代码但不能精细控制排序逻辑的结合。第三种是前端JS分页比如DataTables插件一次加载全部数据再前端分页。数据量小的时候体验很好但几百条商品数据量时页面加载会很慢不推荐作为首选。我实际写项目时用第二种方案居多因为毕设商品数量撑死几十上百条性能问题根本不存在代码简洁优先。6.3 表单校验的前后端双重保障很多教程里只做后端校验前端全靠浏览器默认行为这样做演示时如果触发了必填项提示也还算过得去但远远不够精致。完整的方案是双端都做校验。前端校验的目的是即时反馈、节省一次请求。可以手动给表单字段加required、maxlength等属性也可以用jQuery Validate插件。实测下来保持轻量的话用Bootstrap自带validation样式加浏览器原生校验就足够了不需要引额外插件。后端校验的目的是安全性和数据一致性这是绝对不能省的。最正规的方式是ModelForm 自定义clean方法class OrderForm(forms.Form): address forms.CharField(max_length200, label收货地址) phone forms.CharField(max_length11, label联系电话) def clean_phone(self): phone self.cleaned_data.get(phone) if not re.match(r^1[3-9]\d{9}$, phone): raise forms.ValidationError(手机号格式不正确) return phoneclean_phone会在调用is_valid()时自动执行校验不通过会把错误信息挂到对应字段上模板里用{{ form.phone.errors }}即可渲染。需要记住一个原则任何前端校验都可以被绕过后端校验才是数据安全的最后防线所以后端校验必须做全。7. 部署、安全与答辩准备毕设项目的最后三公里7.1 本地调试到部署如何用一套配置切换环境很多学生做完项目就等着答辩演示完全没考虑部署。但如果老师心血来潮让你在服务器上跑一下或者你需要在另一台电脑上演示比如学校机房环境配置就是绕不开的问题。我的建议是至少保证一个能力在全新的Windows或macOS环境下30分钟内搭出运行环境跑通项目。具体的配置流程在项目文档里要写清楚创建虚拟环境、安装依赖、修改数据库连接、迁移、创建超级管理员、启动开发服务器。settings.py里建议配置成通过环境变量读取关键参数比如数据库账号密码import os DEBUG os.getenv(DJANGO_DEBUG, True) True DB_NAME os.getenv(DB_NAME, flower_shop) DB_USER os.getenv(DB_USER, root) DB_PASSWORD os.getenv(DB_PASSWORD, )默认值保证本地开箱即用需要部署时通过环境变量覆盖不需要改代码。这个设计在答辩时可能会被问你怎么区分开发和生产环境回答起来会非常加分。如果时间允许可以进一步把项目部署到服务器上使用Nginx uWSGI的方式运行Django程序。网上相关的完整教程很多照着一步步做大概需要半天到一天。部署成功后可以把地址和效果截图放进PPT这会成为整个答辩的亮点之一。7.2 常见安全漏洞SQL注入、XSS、CSRF在Django里怎么防毕业设计答辩时老师可能会问一个经典问题你这个系统有哪些安全防护措施所以我建议提前了解一下Django框架内置的安全机制。SQL注入Django的ORM全部使用参数化查询天然避免SQL注入。如果你项目里用了rawSQL或者raw()方法就需要小心拼接问题。XSS跨站脚本攻击Django模板默认对变量输出做HTML转义直接在模板中显示用户输入的内容是安全的。危险点在于mark_safe、safe过滤器、以及前端JS的innerHTML赋值。比如商品详情里如果允许用户在内容里插入HTML就需要仔细过滤script标签等恶意代码。CSRF上面提到的CSRF Token就是框架的防线只要不全站禁用CSRF中间件、不在AJAX请求时忽略Token默认是安全的。Session安全建议在settings里开启SESSION_COOKIE_HTTPONLY禁止JavaScript读取Session。密码安全Django默认使用PBKDF2算法存储密码哈希安全性足够。我建议在项目文档的安全小节里把以上三个层面分别写一小段说明配合框架默认配置演示答辩时这部分就会变成你的加分项。7.3 答辩现场最容易翻车的五个问题与应对思路根据我带项目答辩的经验下面这五个问题几乎每位做商城类毕设的同学都会被问到提前准备好答案能大大减少临场紧张。第一问你这个项目的创新点是什么坦诚的应对思路功能框架上是典型的MVC电商系统创新点主要体现在业务细节和技术选型的合理性上比如F表达式实现并发安全的库存扣减PROTECT外键保护数据完整性订单快照设计保证历史数据准确性。这部分前面都展开讲过答辩时有条理地说出来说服力还是足够的。第二问数据库表为什么这样设计直接打开数据模型图从用户、商品、订单三条主线的关联关系讲起突出外键约束策略和冗余字段的设计理由。第三问购物车和订单的数据一致性怎么保证答案指向两条一是事务atomic保证订单创建和库存扣减的原子性二是状态机的转换校验保证订单状态流转合法。第四问如果用户量大了这个系统的性能瓶颈在哪里可以从数据库索引、查询优化、分页加载、缓存引入几个角度回答核心是表达你知道问题在哪里、也知道怎么改而不是假装没有瓶颈。第五问你开发过程中遇到的最大困难是什么说实话往往最打动人可以说首次部署时环境问题排查最花时间之后深入阅读了日志和官方文档才解决。提前想好一个具体的坑比临时编造一个要自然得多。8. 一套可交付的源码和资料从拿到项目到顺利答辩8.1 源码包的目录结构和阅读顺序建议一个精心组织的源码包应该让人拿到手后能按图索骥。目录结构的建议如下项目主目录manage.py、requirements.txt、README.md和整个Django项目文件夹。文档目录需求分析文档、数据库设计文档含ER图、部署说明文档、答辩PPT。演示视频如果录制了系统演示视频放一份截图或视频文件。拿到源码包后的阅读顺序建议是先读README.md了解项目全貌和背景再打开数据库文档看表关系然后跟着代码把用户登录注册、商品展示、购物车、订单、管理后台这条主线走一遍。最后再逐个App看细节这样才能在短时间内理解整套系统的结构逻辑。如果源码是你自己写的建议写一个项目结构导航文档把每个App的models和views里哪个文件对应哪个功能标注清楚。这既方便自己答辩前回顾也方便指导老师归档检查。8.2 文档和代码讲解怎么配合使用动手复现的频率建议毕业论文里的技术文档和实际代码之间存在天然鸿沟很多人文档写得漂亮、代码却跑不通或者反之代码能跑、文档几乎空白。我强烈的建议是文档中每一个核心功能都要对应到具体的代码文件和函数行号最好能截图关键代码加注释。比如讲解订单事务时把transaction.atomic所在的代码块截图旁边标注这里保证库存扣减与订单创建原子操作这样评委能一目了然地对应上。如果随源码附带代码讲解视频那么听讲的节奏应该是每次讲解一个功能模块后自己动手把代码敲一遍或者根据需求做一个小改动。比如讲完商品模型的字段设计后练习给商品表添加一个促销价字段然后重新生成迁移文件、执行迁移、后台注册显示。在真实的代码上做一点小改动理解的深度和只看不写是完全不同的。8.3 运行环境配置与常见报错速查一套源码能否顺利跑起来环境配置往往是第一道关卡。我把最常见的几个报错场景整理成一个速查表方便拿到任何Django项目时快速排错。现象最常见原因处理方法运行manage.py时报ModuleNotFoundError缺少依赖包或Python版本不匹配检查requirements.txt依赖运行pip install -r requirements.txt迁移时报InconsistentMigrationHistory自定义User模型设置过晚在迁移前设置AUTH_USER_MODEL必要时删除数据库重新迁移页面出现TemplateDoesNotExist项目路径下找不到模板目录检查settings.py中DIRS路径以及代码里的相对路径是否正确上传图片后无法显示media文件serving未配置按照前面提到的方式在DEBUG下添加media路由中文乱码数据库字符集或Python文件编码问题检查MySQL数据库连接charset配置设置为utf8mb4登录后页面报User matching query does not exist关联错误自定义User模型和已有数据不一致确认AUTH_USER_MODEL配置正确后重建数据库并重新迁移8.4 如何让一套源码变身真正属于自己的毕设最后这一段是我个人最真实的体会。每年都有人直接拿网上的源码交差往往被老师多追问两句就露馅。如果你拿到源码之后按下面三步走一遍效果会完全不同。第一步是换皮。把项目名称、数据库名、站点标题、商品品类替换成与自己思路一致的内容。如果你把花卉替换成动漫周边核心电商逻辑完全相同但讲解项目背景时会更自信指导老师也能感受到有独立工作量。第二步是加模块。在上面的功能清单里挑一个扩展功能自己从零实现比如优惠券系统、收藏夹、商品评价。不要小看这一个模块它需要你完整走一遍模型设计、视图逻辑、模板渲染、路由配置的全链路。第三步是深挖一两个技术点。选一个老师可能会追问的细节比如前面说的F表达式扣库存或订单快照设计把官网文档和源码读透用自己的话把为什么这样设计讲清楚。做完这三步这套源码就不再是别人的项目而是你能够说清楚设计思路、能够应对追问、能够演示的属于你自己的毕设项目。这套流程我看着很多学生用过效果确实比直接背源码要好得多。
返回列表