ARTICLE DETAIL

资讯详情

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

Python Django电商购物平台毕设开发全攻略:从选型到避坑

Python Django电商购物平台毕设开发全攻略:从选型到避坑 简介这是一份基于Python的电商购物平台完整项目资源面向毕业设计、课程设计及相关方向学习者提供了商城前台与后台管理两套可运行代码及配套说明文档。资源共425个文件、压缩包8.91MB内含10个Python源文件、40个HTML页面、25个CSS样式、11个JS脚本及SQL数据库脚本配合174个GIF操作演示与大量PNG/JPG界面截图目录结构清晰方便按模块快速定位源码、页面与文档。项目前台覆盖用户注册登录、商品分类浏览、购物车结算、模拟支付宝支付、订单查询和关键词搜索后台包含管理员登录与基础管理入口完整呈现电商平台从用户操作到后台维护的闭环流程各个环节均有对应代码和界面可对照。当前已有168人学习使用适合课程设计、毕业设计答辩或二次开发时作为可运行参考方案。1. 用Python做电商购物平台毕设选型前先看清这几件事刚接触“基于Python的电商购物平台”这类项目的人大多卡在同一个地方下载了别人分享的源代码包却连python环境都跑不起来照着 python 安装教程配好环境后又发现数据库连不上。这个项目放在毕业设计和课程设计里本质是“Python 后端 Web 管理后台 数据库设计”的综合练习不是让你堆功能而是要形成一条完整的业务闭环注册登录、浏览商品、加购物车、下订单、模拟支付、后台发货。下文按我带毕设的真实习惯来讲先定技术选型再拆数据表然后逐模块实现最后把最容易翻车的坑提前告诉你。适合两类人一类是两周后要交课程设计的学生另一类是拿别人源码做二次开发、却不知道怎么改的 Python 新手。目标不是让你背代码而是让你能对着自己的文档把每一步为什么这么写讲清楚。2. 技术选型Django还是FlaskORM和支付接口怎么定2.1 两类框架的取舍为什么主流毕设都选Django电商购物平台这个题目看起来功能多实际上落到答辩环节评审老师只关心四件事数据模型有没有设计成两张以上关联表、订单状态会不会乱、权限控制是不是用了现成方案、部署到新机器能不能跑起来。Django 恰好把这四件事都内置了ORM 负责表结构迁移自带用户认证和 Admin 后台模板系统也不需要你自己拼字符串。Flask 虽然轻量但需要自己组装 Flask-SQLAlchemy、Flask-Login、WTForms 等一堆扩展组装过程中任何一个版本对不上就是整晚排查依赖的花式踩坑。课程设计只有几周时间与其在框架选型上玩玄学不如选一个开工就能跑的东西。对比项DjangoFlask用户认证内置 User 模型与登录装饰器需要 Flask-Login 插件ORM模型类直接映射表迁移命令一条龙SQLAlchemy 语法要另学Admin 后台自带 /admin 可视化维护需扩展 flask-admin适合场景有完整后台管理的业务系统轻量 API 服务、快速原型学习曲线初始较重后面省事上手快组装成本高Django 的目录结构本身就是很好的分层示范models.py放数据模型、views.py放业务逻辑、templates/放页面、urls.py做路由。这个结构可以直接画进论文的“系统架构图”详细设计章节能写很多页。另外当你把项目交给别人时Django 项目里的manage.py、settings.py都是约定俗成的名字指导老师看一眼就能明白你的项目组织。如果选 Flask你还需要自己解释为什么用 Blueprint 拆分目录解释成本凭空高了一截。再说python环境本身。这类项目最常卡住的是环境配置我建议先按 python 安装教程里的虚拟环境部分操作用python -m venv venv创建独立环境再pip install项目依赖。很多同学图省事直接pip install django到全局环境结果多个项目共享一套包今天升级这个库明天那个项目就启动不了。源代码管理的第一步不是git init而是把依赖写进requirements.txt让任何人在新机器上都能用三行命令跑起来python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt这段的命令含义第一步创建虚拟环境第二步激活它第三步按清单安装包。requirements.txt不是手写的而是pip freeze requirements.txt生成它会把当前环境里所有包和精确版本号都记录下来。2.2 数据模型与购物车状态存储先画好这四张表电商平台再怎么扩展核心模型仍是用户、商品、购物车、订单这四张基础表。第一次做很容易犯的错是一上来就建十几张表促销、优惠券、收藏夹统统加进去结果外键关系自己都说不清答辩被问一句“为什么订单表里没有商品快照”就愣住。我的习惯是先建最小闭环再往里面扩展。商品表要拆 SPU 和 SKU。SPU 是“一件商品”SKU 是“一个具体规格”比如“iPhone 15”是 SPU“iPhone 15 256G 黑色”是 SKU。毕设里如果只做简单演示可以只建一张商品表把规格字段直接放进去但论文里想体现数据库设计能力拆成两张表是标准答案。SKU 表里存price、stock、spec商品表存标题、描述、封面图、分类外键下面是一个可以直接放进项目的模型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 Product(models.Model): # SPU 粒度标题、描述、默认图片 title models.CharField(max_length200, verbose_name商品标题) description models.TextField(blankTrue, verbose_name商品描述) cover models.ImageField(upload_toproduct/cover/%Y/%m/, verbose_name封面图) category models.ForeignKey(Category, on_deletemodels.SET_NULL, nullTrue, verbose_name分类) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Sku(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, related_nameskus, verbose_name所属商品) spec models.CharField(max_length100, verbose_name规格名) price models.DecimalField(max_digits10, decimal_places2, verbose_name售价) stock models.PositiveIntegerField(default0, verbose_name库存) status models.BooleanField(defaultTrue, verbose_name是否上架)关键参数逐个解释on_deletemodels.CASCADE在删除商品时把它的 SKU 一并删掉防止孤儿数据related_nameskus让代码里可以写product.skus拿到该商品全部规格DecimalField而不是FloatField因为浮点数存金额会出现 0.1 加 0.2 不等于 0.3 的精度问题upload_to里的%Y/%m/会自动按年月生成子目录方便日后清理过期图片。购物车可以放 session也可以建表。如果只服务当前登录用户session 方案最简单但你的订单模块需要复用“购物车里勾选的商品”还要统计哪些用户长时间没结算建表更合适。购物车表通常长这样class CartItem(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name用户) sku models.ForeignKey(Sku, on_deletemodels.CASCADE, verbose_nameSKU) quantity models.PositiveIntegerField(default1, verbose_name数量) checked models.BooleanField(defaultTrue, verbose_name是否勾选) created_at models.DateTimeField(auto_now_addTrue, verbose_name加入时间)这里checked字段是为了实现“勾选部分商品去结算”的前端交互。很多教程里把数量直接存在 session一旦用户换设备就丢失答辩时容易被问倒。建表之后你还可以在文档里写购物车表通过外键关联用户和 SKU实现了“用户-商品”多对多关系的中间表语义。订单表要拆主表和明细表。主表存订单号、用户、总金额、状态明细表存每一件商品的 SKU、单价、数量、小计。为什么不只建一张表因为订单主表一行对应多个商品如果冗余在同一行后面做退款、发货、统计都很难处理。四张表的关系清楚之后论文的 ER 图用 draw.io 画一页就够。2.3 订单状态机从待付款到已完成的状态流转规范这是评审老师最愿意追问的设计点。订单状态如果只是随便存个字符串后面退款、取消、发货会互相踩踏。我的做法是在models.py里定义状态常量用choices限定取值范围再单独写一个状态转换服务方法。class Order(models.Model): class Status(models.TextChoices): PENDING_PAYMENT PENDING_PAYMENT, 待付款 PAID PAID, 待发货 SHIPPED SHIPPED, 待收货 COMPLETED COMPLETED, 已完成 CANCELLED CANCELLED, 已取消 REFUNDING REFUNDING, 退款中 order_no models.CharField(max_length32, uniqueTrue, verbose_name订单号) user models.ForeignKey(User, on_deletemodels.PROTECT, verbose_name下单用户) status models.CharField(max_length32, choicesStatus.choices, defaultStatus.PENDING_PAYMENT, verbose_name状态) total_amount models.DecimalField(max_digits10, decimal_places2, verbose_name总金额) created_at models.DateTimeField(auto_now_addTrue, verbose_name下单时间)status用CharField而不是BooleanField因为状态会越来越多布尔值根本无法表达中间状态。choicesStatus.choices是 Django 3.0 之后推荐的写法它会在 Admin 后台里生成下拉框同时数据库层面限定取值范围。这里on_deletemodels.PROTECT是个容易被忽略的细节订单外键到用户如果用户被删除订单会失去归属用PROTECT会阻止删除保证订单历史可追溯。状态流转要显式判断。比如“待付款”只能到“已取消”或“已付款”“已付款”不能跳回“待付款”。我会用一个字典定义允许的迁移路径TRANSITION_MAP { PENDING_PAYMENT: (CANCELLED, PAID), PAID: (SHIPPED, REFUNDING), SHIPPED: (COMPLETED, REFUNDING), REFUNDING: (COMPLETED, CANCELLED), } def change_order_status(order, new_status): allowed TRANSITION_MAP.get(order.status, ()) if new_status not in allowed: raise ValueError(f订单状态不允许从 {order.status} 变更为 {new_status}) order.status new_status order.save(update_fields[status])这段代码的逻辑每次状态变更前都先查字典不在允许路径里就直接抛异常禁止在业务视图里随手写order.status PAID绕过校验。update_fields[status]只更新状态字段避免把created_at等字段无意义地重写也减少 SQL 语句大小。答辩时你可以顺势讲这就是状态机在业务层的落法以后要加“已申请发票”状态只需要在TRANSITION_MAP加一条。为什么把change_order_status单独拿出来因为支付回调、取消订单、发货操作、退款审批都要改订单状态如果每个视图自己写一遍判断逻辑就分散了。把它收敛成一个函数后整个项目的状态变更入口只有一个测试也只需要针对这个函数写。加pytest时这就是第一个单测对象。3. 从零搭起用户与商品模块注册、登录、商品列表的最小实现3.1 用Django自带User模型扩展出买家与卖家很多电商毕设要求区分普通用户和管理员其实完全没必要自己写用户表。Django 自带的User模型已经包含用户名、密码哈希、邮箱、权限标志直接扩展出一张 Profile 表用 OneToOne 关联就够覆盖“买家/卖家/收货地址”这些业务需求。这样后台登录、密码重置、session 管理全是现成的你不需要把时间浪费在重造轮子上。from django.contrib.auth.models import User from django.db import models class Profile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile, verbose_name用户) user_type models.CharField(max_length10, choices[(buyer, 买家), (seller, 卖家)], defaultbuyer, verbose_name用户类型) phone models.CharField(max_length20, blankTrue, verbose_name手机号) address models.CharField(max_length255, blankTrue, verbose_name默认收货地址)OneToOneField保证一个User只有一条 Profile访问时写作request.user.profile。需要注意只有登录后才有request.user未登录时访问会抛RelatedObjectDoesNotExist所以视图里要先login_required。user_type这个字段决定了你后续页面跳转是进买家中心还是卖家后台。这里可以做一个信号处理器在用户注册时自动建 Profile但毕设阶段直接在注册视图里同步创建就够了不值得为它引入 signal 机制。注册视图的关键是密码处理。一定不能用User.objects.create()因为 create 不哈希密码会在数据库里存明文密码。标准写法如下from django.contrib.auth import authenticate, login from django.contrib.auth.models import User from django.shortcuts import render, redirect from .models import Profile def register(request): if request.method POST: username request.POST.get(username) password request.POST.get(password) if User.objects.filter(usernameusername).exists(): return render(request, register.html, {error: 用户名已存在}) user User.objects.create_user(usernameusername, passwordpassword) Profile.objects.create(useruser, user_typebuyer, phonerequest.POST.get(phone, )) login(request, user) return redirect(product_list) return render(request, register.html)这里create_user会自动调用set_password对密码做 PBKDF2 哈希。Profile.objects.create在用户表创建成功后执行。登录则用authenticate校验用户名密码再用login写入 session不要手工设置request.session[user_id]否则权限判断会漏。很多 python 入门教程里只写create不写create_user这个坑你自己写的时候要避掉。3.2 商品SPU/SKU拆分与图片上传路径回到第二章创建的Product和Sku商品管理页面主要做两件事先填 SPU 的标题、描述、封面图再添加一组 SKU 的规格、价格和库存。前端表单必须带上enctypemultipart/form-data属性否则文件到达不了 Django 的request.FILES。很多同学在这里浪费一下午就是因为form标签里少了这个属性图片上传接口永远返回空。图片上传后Django 的ImageField会把它保存到MEDIA_ROOT指定目录。settings.py里需要做两处配置MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media然后在项目的urls.py里增加一个开发环境专用的静态服务路由from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)MEDIA_ROOT是文件实际落盘的位置BASE_DIR / media会拼出当前项目下的media文件夹这样换机器不会出现路径找不到。MEDIA_URL是浏览器访问的 URL 前缀模板里写img src{{ product.cover.url }}时输出的是类似/media/product/cover/2026/05/xxx.png。开发环境不加static(...)这一行上传的图片在页面上会 404而报错日志里又看不到异常这是典型的“黑匣子”问题。这里还要说明文件命名Django 默认会帮你生成随机文件名避免中文文件名在 URL 里乱码。但如果你需要在文档中说明“文件存储规划”建议自己写一个upload_to可调用对象把商品 ID 拼进文件名让图片命名与业务绑定后期导出数据时不容易对不上。3.3 商品搜索与分类过滤的ORM写法首页商品列表是整个项目最直观的功能。新手容易在视图里把全部商品查出来然后在 Python 里用 for 循环做过滤数据量一大页面就直接变慢。正确做法是让 ORM 把过滤、排序翻译成 SQL 执行。这里用 Django 的Q对象处理关键词用filter处理分类用prefetch_related预取 SKU 数据避免循环查询。from django.db.models import Q from django.views.generic import ListView from .models import Product class ProductListView(ListView): model Product template_name shop/product_list.html paginate_by 12 def get_queryset(self): qs Product.objects.prefetch_related(skus).filter(skus__statusTrue).distinct() keyword self.request.GET.get(q, ).strip() if keyword: qs qs.filter(Q(title__icontainskeyword) | Q(description__icontainskeyword)) category_id self.request.GET.get(category) if category_id: qs qs.filter(category_idcategory_id) return qs.order_by(-created_at)解释几个关键参数prefetch_related(skus)会在一次查询里把所有 SKU 取出来模板中遍历product.skus时不再产生额外查询这叫预取能显著降低数据库压力。skus__statusTrue表示只要存在上架 SKU 的商品但一个商品可能对应多个 SKUJOIN 之后会产生重复行所以要用distinct()去重。icontains是不区分大小写的模糊匹配中文搜索同样适用。paginate_by 12由 Django 自带分页器完成模板里用page_obj渲染上一页和下一页。这节实现完以后你可以用 Django Debug Toolbar 查看每一次请求的 SQL 数量。没加预取时商品列表每显示一条商品就要多查一次 SKU20 条商品就是 21 条 SQL加上预取后通常稳定在 5 条以内。这个对比写进文档比空谈“性能优化”有说服力得多。4. 购物车与订单流程把并发扣库存的坑提前填上4.1 购物车用session还是Redis缓存购物车是最值得写的业务模块因为它在存储方案上有一个经典选择session 还是 Redis。用 session 的好处是依赖少项目在本地跑起来不需要额外服务坏处是不能跨设备同步而且数据存在服务端session 过期就丢。用 Redis 的好处是独立于应用进程、支持过期策略可以做到“一周内有效”坏处是文档里要多写一段 Redis 的安装、启动和连接配置答辩现场如果服务没起来页面一刷新购物车就空场面会很尴尬。我的建议是课程设计阶段用 session 实现把“为什么不用 Redis”写成系统扩展点。这有两个好处一是核心逻辑不用引入额外组件跑通优先二是在论文的“不足与展望”里留一个“可以迁移到 Redis”的改进点反而显得你有大局观。session 的写法也很简单cart request.session.get(cart, {}) sku_id str(sku.id) if sku_id in cart: cart[sku_id][quantity] int(quantity) else: cart[sku_id] {quantity: int(quantity), add_time: str(time.time())} request.session[cart] cart request.session.modified True这里的要点sku_id必须转成字符串因为 session 在存储前会做 JSON 序列化数字键会被转成字符串不转的话下次判断sku_id in cart时可能匹配不上。request.session.modified True是告诉 Django 会话内容变了需要重新写回如果没有这一行某些配置下 session 不会被保存你加购了商品刷新页面又变空这是典型的“玄学翻车”。add_time字段将来可以用于“购物车超时自动清理”也是一个设计点。如果选择 Redis代码会变成cart_key fcart:{user.id}然后redis.hset(cart_key, sku_id, quantity)还要处理hincrby的原子自增。这里不展开因为毕设现场跑 Redis 容易节外生枝。你只需要在文档中写清楚Redis 方案适合多端同步和持久化生产环境应当使用就够了。4.2 订单生成与库存扣减事务与锁提交订单最怕“超卖”两个用户同时买最后一件商品都读到了库存 1都判断有货都扣减成功库存变成 -1。这个问题只在并发时出现本地单点测试很难复现所以很多人准备答辩时根本不重视直到老师问“如果两个人同时下单怎么办”才发晕。正确做法是数据库行锁配合事务。from django.db import transaction transaction.atomic def create_order(user, sku_items): order Order.objects.create( useruser, order_nogenerate_order_no(user.id), statusPENDING_PAYMENT ) total 0 for sku_id, quantity in sku_items: # 锁住当前 SKU 行直到事务结束 sku Sku.objects.select_for_update().get(pksku_id) if sku.stock quantity: raise ValueError(f库存不足当前剩余 {sku.stock}) sku.stock - quantity sku.save(update_fields[stock]) # 继续写订单明细这里省略明细模型的写入 total sku.price * quantity order.total_amount total order.save(update_fields[total_amount]) return ordertransaction.atomic保证整个函数要么全部成功要么全部回滚。select_for_update()是数据库层面的行锁它会在查 SKU 时请求一个排他锁其他事务要更新同一行必须等这次事务结束。顺序很重要必须先锁再判断如果代码里先get再select_for_update锁就晚了一步。sku.stock - quantity是对 Python 对象的操作真正写库是save但锁定的范围是整个事务所以并发安全。为什么要用select_for_update而不是update的原子操作Sku.objects.filter(pksku_id).update(stockF(stock) - quantity)也能减库存但它无法在减之前判断“库存是否够用”需要先查再更新两步之间会有窗口期。select_for_update把“查询-判断-更新”锁成了一片才是完整的临界区保护。这里还会遇到一个细节事务函数里如果抛出了ValueErrorDjango 会在事务结束时回滚但如果你想向用户展示“库存不足”的提示需要在视图层 catch 这个异常而不是让它直接变成 500。实际项目里会把ValueError换成自定义的BizError然后统一在中间件处理毕设里直接用try-except展示错误消息已经够了。create_order函数里不要再嵌套其他事务标记嵌套多个atomic会改变保存点行为初学者最容易在这里看晕。4.3 模拟支付回调本地开发怎么验签毕设接真实支付通道没有意义因为需要商户号、密钥、域名备案流程长到根本做不完。一般的做法是做一个模拟支付页面用户点“去支付”跳到一个本地支付页输入任意卡号点“确认支付”系统调一个模拟回调把订单状态改成已付款。这个流程贴近真实支付而且能让你在文档里写出“支付模块设计”。关键不在于页面长什么样而在于回调接口怎么验签。真实支付回调会带签名参数作为开发者必须验签后才允许改状态否则别人随便 POST 一个order_no就能把你的订单变已付款。本地模拟也要做签名哪怕简单点思路不能丢。import hashlib from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt PAYMENT_SALT course-design-demo-salt csrf_exempt def mock_payment_callback(request): if request.method POST: order_no request.POST.get(order_no) amount request.POST.get(amount) raw f{order_no}{amount}{PAYMENT_SALT} sign hashlib.md5(raw.encode()).hexdigest() if request.POST.get(sign) ! sign: return JsonResponse({code: 402, msg: 验签失败}, status400) order Order.objects.select_for_update().get(order_noorder_no) if order.total_amount ! float(amount): return JsonResponse({code: 402, msg: 金额不符}, status400) change_order_status(order, PAID) return JsonResponse({code: 200, msg: success}) return JsonResponse({code: 405}, status405)这里几个点要讲给答辩老师听csrf_exempt是必须的因为真实支付服务器不会带着 Django 页面生成的 CSRF token 来这是外部系统回调的常态验签用 MD5 加固定盐值只是教学演示真实场景要用 HMAC-SHA256 或 RSA 验签收到回调先查订单状态只有PENDING_PAYMENT才能改成PAID重复回调不会重复改状态订单总金额也要比对防止回调被篡改。select_for_update在这里再次出现是为了防止两个请求同时处理同一个订单造成状态覆盖。前端支付页的“支付成功”按钮其实只是发一个 AJAX 到本地模拟支付接口这个接口再调用上面的回调地址。真实流程是支付平台请求你的服务器演示时直接在支付视图里用requests.post调用回调本身也能把流程走通。体验上你可以加一个 1 到 2 秒的time.sleep模拟网络延迟看起来更真实但不是必须。5. 毕设避坑数据库迁移、时区、密码重置和源代码管理的八个常见问题这一章按现象、原因、解决的顺序写全是实际带毕业生过程中反复出现的踩坑记录。每一条都可以直接摘一段放进论文的“系统调试”章节。5.1 迁移文件冲突makemigrations 报错现象项目从 A 电脑复制到 B 电脑后执行python manage.py migrate提示“迁移目录不存在”或“表已存在”。原因复制项目时漏掉了migrations/文件夹或者旧数据库里已经有表但新的迁移记录又要建一次。很多同学打包源码时只挑.py文件migrations目录因为名字是数字开头容易被忽略。解决先确认数据库文件状态如果要重置把db.sqlite3删掉。然后依次执行python manage.py makemigrations python manage.py migrate如果已经跑过部分迁移想回退python manage.py migrate myapp 00010001是第一个迁移文件的编号回退再重新迁移即可。注意不要手动去django_migrations表里删记录Django 的迁移状态和数据库结构一旦不一致后面所有migrate都会卡死在同一条提示上。5.2 时区设置导致订单时间错乱现象页面显示下单时间是 14:00数据库里存的是 21:00或者 Admin 后台看到的时间比实际晚 8 小时。原因Django 默认TIME_ZONE UTC你的机器在东八区。数据库统一按 UTC 存储展示时不转换就会出现 8 小时差。解决在settings.py里改成TIME_ZONE Asia/Shanghai USE_TZ True这样 Django 会在渲染模板时自动把存储的 UTC 时间转换成上海时间。还有一个别忽略的点代码里创建时间要使用django.utils.timezone.now()不要用原生datetime.now()因为USE_TZTrue时数据库期望带时区的时间对象直接存 naive 时间会出警告订单排序也会错。5.3 密码重置邮件发不出去现象在生产环境点“忘记密码”页面白屏或报 SMTP 错误本地没有邮件服务器。原因Django 默认用 SMTP 后端发邮件而本机没有配任何邮箱账号。课程设计阶段完全没必要为了这功能去注册一个邮箱服务。解决在settings.py里切换到 console 后端EMAIL_BACKEND django.core.mail.backends.console.EmailBackend之后点击“获取重置链接”邮件内容会直接打印在启动runserver的控制台里复制里面的链接就能完成重置。这个方案零延迟适合演示。文档里要注明生产环境应该用 SMTP 后端并配置EMAIL_HOST等参数。5.4 源代码管理漏掉关键文件现象把项目压缩包发给别人对方解压后执行pip install装依赖报错数据库也没法初始化。原因压缩包或 Git 仓库里只有app目录缺少requirements.txt、README.md甚至没有manage.py的启动说明。源代码管理不只是管源码还包括环境声明和启动手册。解决项目根目录执行pip freeze requirements.txt然后建一个README.md按 python 安装教程的步骤写清创建虚拟环境、激活、安装依赖、迁移、创建超级用户、启动开发服务器。如果你的课程设计要提交“源代码文档”这两个文件比任何一段代码都值钱因为指导老师拿到手第一件事就是按 README 复现。5.5 静态文件和图片路径问题现象登录页样式全裸图片不显示F12 看到/static/css/bootstrap.css404。原因DEBUGTrue或False时的静态文件服务方式不同你需要在urls.py里主动配置开发环境的静态路由。解决settings.py里配置STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]在urls.py里和 media 路由一起追加urlpatterns static(settings.STATIC_URL, document_rootsettings.BASE_DIR / static)模板里加载静态文件用{% load static %}和{% static css/bootstrap.css %}不要硬写/static/...相对路径。很多页面引用多个样式文件漏配一个目录就会部分生效部分 404。5.6 Admin 后台密码忘了现象很久以前创建了超级用户密码怎么都想不起来/admin进不去。原因Django 用户表里保存的是 PBKDF2 哈希不能用明文去数据库倒推。解决在项目根目录打开 shellpython manage.py shell执行from django.contrib.auth.models import User u User.objects.get(usernameadmin) u.set_password(newpass123) u.save()set_password会重新哈希save后立即生效。再强调一次不要直接改password字段自己拼一串字符存进去你会发现永远登不上这是必踩的新手坑。5.7 模板变量查不到外键对象现象模板里写了{{ order.user.username }}页面上是空白后端日志没有任何报错。原因Django 模板对不存在的属性会静默渲染成空字符串不会抛AttributeError。常见原因是order.user本身是空对象或者字段名写错成order.username。解决在视图层临时输出一行调试或者用 shellpython manage.py shell输入order Order.objects.first() print(order.user_id)user_id是外键字段在数据库里的名字如果它是空值说明订单没有关联上用户如果非空就检查模板变量名。前台的用户模块和后台的订单模块是两个上下文别把变量名搞混。这里可以直接用 Django 的{% if order.user %}做保护让页面在异常数据时不至于白屏。5.8 依赖包安装后项目仍报 ModuleNotFoundError现象pip install django显示成功但python manage.py还是提示ModuleNotFoundError: No module named django。原因pip 装进了全局 Python而项目使用的是虚拟环境或当前终端里激活了别的虚拟环境。常见于系统里有多个 Python 版本并存、PATH 指向旧环境。解决先执行which python which pip确认两者是否都指向同一个虚拟环境目录均包含venv/bin。如果没激活就source venv/bin/activate # Windows: venv\Scripts\activate python -m pip install --upgrade pip pip install -r requirements.txt如果 PyCharm 或 VSCode 里选择了不同的解释器也会造成同样现象。这道坑写进文档里指导老师会觉得你把环境问题处理得很清楚。6. 把项目做成能答辩的样子测试数据、调试工具和演示技巧6.1 用 fixture 一次性生成可复现的演示数据答辩最怕演示时数据库空无一物或者临时手滑删了库全场等你慢慢重新录入。做法是把演示数据导出成 fixture 文件放到项目仓库里。导出命令python manage.py dumpdata --indent 2 shop shop_fixture.json恢复命令python manage.py loaddata shop_fixture.jsonshop是你的应用名--indent 2只是让 JSON 可读。我要特别提醒fixture 带有主键和外键 ID所以每次演示前最好删掉db.sqlite3重新migrate再loaddata保证数据状态和第一次一样。不要一边演示一边往库里加测试数据下次加载时主键冲突会让迁移崩溃。6.2 用 Django Debug Toolbar 撑起演示答辩最怕被问“性能优化做了什么”除了答“用了分页和预取”还可以直接打开工具栏展示 SQL 次数。安装pip install django-debug-toolbar配置INSTALLED_APPS [ ... debug_toolbar, ]然后在urls.py加路由from debug_toolbar.toolbar import debug_toolbar_urlpatterns urlpatterns debug_toolbar_urlpatterns()配置完成后页面右侧会显示当前页面的 SQL 列表、耗时和重复查询。演示时切到商品搜索页用工具栏指出“21 次查询中有 18 次重复”再说已经用prefetch_related优化过这个演示比口头解释强很多。注意 DEBUG 为 True 时才会显示如果答辩前为了“安全”把 DEBUG 设为 False工具栏就消失了。6.3 演示前必调的三个参数ALLOWED_HOSTS、DEBUG 和 MEDIA 路径准备收尾我会带着学生检查三件事。第一ALLOWED_HOSTS。在DEBUG False时必须配域名或 IP否则启动后页面直接 400如果是本机演示ALLOWED_HOSTS [127.0.0.1, localhost]就够了不要用[*]图省事这在文档里不好解释。第二DEBUG。答辩机想用工具栏就开着 True不想开就设成 False演示时注意别因为 False 导致静态文件挂掉提前跑一遍collectstatic。第三MEDIA_ROOT不要指向自己桌面的绝对路径用BASE_DIR / media相对写法。这三个参数是我每次演示前必查的固定动作检查完才能安心点“开始演示”。这个项目我带过的学生里最多的问题不是代码不会写而是没提前走完整链路。演示前一天把数据库删掉重新 migrate导入 fixture注册一个新账号加购下单支付发货确认收货全流程走一遍并把每一步截图放到文档里。截图不算装饰它让答辩老师直观看到功能闭环。最深的教训是不要临时改模型。答辩前一周如果还在加字段迁移文件一乱整个进度都会崩。合上电脑之前记得把requirements.txt重新生成一遍把shop_fixture.json备份一次。希望这个方案能帮到你照着走至少能让你在答辩台上从容地把自己的代码讲通。本文还有配套的精品资源点击获取
返回列表