
简介这是一套面向高校软件工程、计算机等专业本科毕业设计的完整项目源码采用微信小程序前端与Python-Django后端的前后端分离架构实现餐厅选择、菜品浏览、购物车管理、在线支付与订单查询等在线点餐核心流程同时提供商家端的菜品管理、订单处理与数据统计功能适合需要完成毕设或学习小程序与Django协同开发的中级开发者参考。压缩包共约2000个文件以1474个py后端逻辑、201个html模板、127个js脚本为主辅以wxss、wxml小程序页面样式与结构文件以及css、json、txt等配置与说明文档整体约18.39MB目录结构清晰便于按模块定位学习。目前已有97人学习下载。资源完整覆盖需求分析、接口设计到功能实现的全流程读者可据此理解RESTful API交互、MySQL数据存储与订单状态同步等关键实现思路并作为前后端协同开发的实践案例参考。1. 从一份「在线点餐.zip」说起小程序点餐到底难在哪打开这个压缩包之前先想清楚一件事一个能跑起来的在线点餐系统真正的工作量从来不在「点餐」这两个字上。前端微信小程序负责菜单展示、购物车加减、下单确认后端 pythondjango 负责菜品数据、订单落库、状态流转。听起来是标准的 CRUD但真做起来翻车点集中在三处小程序请求封装没做好导致每个页面都在重复写 wx.requestdjango 的模型关系没设计好订单和菜品之间用错关联字段后期改一处崩三处以及最容易被忽略的——下单那一刻的并发和状态一致性。这份「毕设前端微信小程序后端 pythondjango在线点餐.zip」对应的就是这样一个典型场景一个学生或初级开发者用微信小程序做用户端用 django 做服务端实现扫码点餐、加购物车、提交订单、后台查看订单的完整链路。它适合正在找微信小程序项目实例练手的人也适合想用 django 项目实战把前后端串起来的新手。接下来我不讲空泛的架构图只讲这套东西怎么从零跑通、参数怎么设、哪里会踩坑。2. 环境搭建与项目骨架把 django 后端和小程序前端接上2.1 后端选型为什么是 django 而不是 flask在线点餐这类项目数据模型天然是关系型的菜品属于某个分类订单包含多个菜品每个菜品有数量。django 自带 ORM 和 admin 后台意味着你写完模型就能直接进后台加菜品数据不用再单独写一套管理接口。这是它比 flask 更适合毕设场景的核心原因——flask 更轻但你要自己搭 admin、自己处理迁移工作量反而更大。python 环境建议 3.8 以上django 用 3.2 LTS 或 4.x 都行别用太新的版本部分第三方库还没跟上。先建虚拟环境再装依赖这是血泪经验全局装包后期换机器必翻车。# 创建项目目录并进入 mkdir online_order cd online_order # 创建虚拟环境windows 用 python -m venv venv python3 -m venv venv source venv/bin/activate # 安装 django 和跨域支持 pip install django4.2 django-cors-headers # 创建 django 项目注意结尾的点避免多一层目录 django-admin startproject config . # 创建核心 app python manage.py startapp orders逻辑说明startproject config .里的点很关键它让 manage.py 直接落在当前目录而不是再套一层 config 文件夹。orders这个 app 承载菜品、订单、订单明细三个模型。django-cors-headers 是为了让小程序开发工具里的请求能跨域打到本地后端不装的话前端会报跨域错误。参数说明django 版本选 4.2 是因为它是 LTS长期支持网上教程多遇到问题好搜。虚拟环境目录名用 venv 是约定俗成别改成中文。2.2 数据模型设计三个模型撑起整个点餐流程模型设计是这套系统最容易埋雷的地方。常见错误是把订单和菜品直接做成多对多结果没法记录「这道菜点了 3 份」这种数量信息。正确做法是拆出订单明细表用外键分别指向订单和菜品数量存在明细表里。# orders/models.py from django.db import models class Category(models.Model): 菜品分类比如热菜、凉菜、饮品 name models.CharField(max_length50, verbose_name分类名) sort models.IntegerField(default0, verbose_name排序) class Meta: ordering [sort] def __str__(self): return self.name class Dish(models.Model): 菜品 category models.ForeignKey(Category, on_deletemodels.CASCADE, related_namedishes) name models.CharField(max_length100, verbose_name菜名) price models.DecimalField(max_digits8, decimal_places2, verbose_name价格) image models.CharField(max_length255, blankTrue, verbose_name图片地址) is_active models.BooleanField(defaultTrue, verbose_name是否上架) def __str__(self): return self.name class Order(models.Model): 订单主表 STATUS_CHOICES ( (0, 待支付), (1, 已支付), (2, 已完成), (3, 已取消), ) table_no models.CharField(max_length10, verbose_name桌号) total_price models.DecimalField(max_digits10, decimal_places2, default0) status models.IntegerField(choicesSTATUS_CHOICES, default0) created_at models.DateTimeField(auto_now_addTrue) class OrderItem(models.Model): 订单明细记录每道菜点了几份 order models.ForeignKey(Order, on_deletemodels.CASCADE, related_nameitems) dish models.ForeignKey(Dish, on_deletemodels.PROTECT) quantity models.IntegerField(default1) price models.DecimalField(max_digits8, decimal_places2, verbose_name下单时单价)逻辑说明OrderItem里单独存了一份price这是关键设计。菜品的价格会变但订单一旦生成历史价格必须冻结否则改一次菜价所有历史订单金额全乱。on_deletemodels.PROTECT表示菜品被订单引用后不允许删除防止删菜导致订单明细悬空。参数说明DecimalField用于金额别用 FloatField浮点误差在算总价时会让你怀疑人生。related_name让反向查询更顺比如order.items.all()直接拿到明细。2.3 小程序请求封装一次封装全局复用微信小程序里如果每个页面都写一遍wx.request后期改接口地址要改几十处。标准做法是封装一个 request 工具统一处理 baseURL、token、错误提示。// utils/request.js const BASE_URL http://127.0.0.1:8000/api; function request(options) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200) { resolve(res.data); } else { wx.showToast({ title: 请求失败, icon: none }); reject(res); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); } module.exports { request };逻辑说明用 Promise 包一层页面里就能用await request({url:/dishes/})这种写法比回调清爽。header 里带 token 是为了后续做登录态毕设阶段可以先留空。BASE_URL 指向本地 django 的 8000 端口真机调试时要换成局域网 IP这是新手最常卡住的地方。参数说明content-type用 application/jsondjango 那边要配合解析 JSON body。如果后端用表单格式这里要改成application/x-www-form-urlencoded两边必须一致否则后端收到的 data 是空的。3. 接口实现菜品列表、购物车与下单落库3.1 菜品列表接口与序列化django 里写接口用原生 View 或 DRF 都行。毕设规模不大用 django 自带的 JsonResponse 手写反而更透明能看清每一步在干什么。# orders/views.py from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import Category, Dish, Order, OrderItem import json def dish_list(request): 返回按分类分组的菜品列表 categories Category.objects.prefetch_related(dishes).all() data [] for cat in categories: dishes [{ id: d.id, name: d.name, price: float(d.price), image: d.image, } for d in cat.dishes.filter(is_activeTrue)] data.append({category: cat.name, dishes: dishes}) return JsonResponse({code: 0, data: data})逻辑说明prefetch_related是性能关键。如果不加循环里每次cat.dishes.filter()都会打一次数据库分类一多就是 N1 查询接口慢到肉眼可见。加了之后 django 会一次性把关联数据捞出来。参数说明返回结构用{code, data}是常见约定code 为 0 表示成功前端统一判断。价格转 float 是因为 JSON 不认 Decimal但注意这只用于展示计算仍用 Decimal。3.2 下单接口事务与总价计算下单是整个系统最不能出错的地方。用户提交的是菜品 id 和数量服务端必须重新查价格、算总价、写订单和明细整个过程放在一个事务里。# orders/views.py 续 from django.db import transaction csrf_exempt def create_order(request): if request.method ! POST: return JsonResponse({code: 1, msg: 方法不允许}) body json.loads(request.body) table_no body.get(table_no) items body.get(items, []) # [{dish_id: 1, quantity: 2}, ...] if not items: return JsonResponse({code: 1, msg: 购物车为空}) try: with transaction.atomic(): order Order.objects.create(table_notable_no, total_price0) total 0 for item in items: dish Dish.objects.get(iditem[dish_id], is_activeTrue) qty int(item[quantity]) OrderItem.objects.create( orderorder, dishdish, quantityqty, pricedish.price ) total dish.price * qty order.total_price total order.save() return JsonResponse({code: 0, order_id: order.id, total: float(total)}) except Dish.DoesNotExist: return JsonResponse({code: 1, msg: 菜品不存在或已下架})逻辑说明transaction.atomic()保证要么全部成功要么全部回滚。如果循环到一半某个菜品查不到前面创建的订单和明细会一起撤销不会留下脏数据。总价在服务端算绝不信任前端传来的金额这是安全底线。参数说明items是数组每个元素含 dish_id 和 quantity。Dish.objects.get带is_activeTrue防止用户下单已下架菜品。价格用dish.price而非前端传的杜绝篡改。3.3 路由配置与联调# config/urls.py from django.contrib import admin from django.urls import path from orders import views urlpatterns [ path(admin/, admin.site.urls), path(api/dishes/, views.dish_list), path(api/order/create/, views.create_order), ]逻辑说明把接口挂在/api/前缀下和前端封装的 BASE_URL 对应。admin 路径保留方便进后台加菜品数据。参数说明联调时先python manage.py makemigrations python manage.py migrate建表再python manage.py createsuperuser建管理员然后python manage.py runserver 0.0.0.0:8000启动。用 0.0.0.0 是为了让局域网内手机也能访问只写 127.0.0.1 的话真机调试连不上。4. 避坑与排查那些让毕设卡三天的真实问题4.1 小程序请求本地后端报跨域或连接失败现象开发者工具里请求http://127.0.0.1:8000提示跨域或者真机上完全没反应。原因开发者工具默认校验合法域名本地 IP 不在白名单真机则是因为手机和电脑不在同一网络或者后端只监听了 127.0.0.1。解决开发者工具里勾选「不校验合法域名」真机调试时后端用0.0.0.0:8000启动前端 BASE_URL 换成电脑的局域网 IP用 ipconfig 或 ifconfig 查并确保手机连的是同一个 WiFi。4.2 下单后总价对不上差几分钱现象前端算的金额和后端返回的总价差 0.01 元。原因前端用 JavaScript 的浮点数算钱0.1 0.2 ! 0.3是经典问题。解决金额计算全部放后端用 Decimal前端只负责展示后端返回的 total。如果前端必须临时算把金额乘以 100 转成整数分再算最后除回去。4.3 订单明细查不到菜品名称现象后台看订单只能看到 dish_id看不到菜名。原因序列化时只取了外键 id没做关联查询。解决查询明细时用OrderItem.objects.select_related(dish)然后取item.dish.name。select_related 针对外键prefetch_related 针对多对多和反向外键用错了两者都会导致查询变慢。4.4 修改菜品价格后历史订单金额变了现象把某道菜从 20 元改成 25 元之前订单的总价也跟着变了。原因订单明细里没存下单时的价格展示时实时去查菜品当前价。解决就是 2.2 节里OrderItem.price字段的作用下单时把dish.price快照存进去之后展示一律用这个快照价和菜品表彻底解耦。4.5 并发下单导致库存或金额异常现象两个人同时点最后一份菜都下单成功。原因没有做库存扣减的原子操作或者事务隔离级别不够。解决毕设阶段如果涉及库存用Dish.objects.select_for_update().get(id...)在事务里锁行再判断库存。不涉及库存的话至少保证总价计算在事务内完成避免读到中间状态。5. 进阶技巧用 django admin 和缓存把后台效率提上去毕设答辩时老师大概率会问「后台怎么管理菜品」。与其自己写一套增删改查页面不如把 django admin 用好这是 django 相对其他框架的独门优势。在orders/admin.py里注册模型并做简单定制就能得到一个可用的管理后台。# orders/admin.py from django.contrib import admin from .models import Category, Dish, Order, OrderItem class OrderItemInline(admin.TabularInline): model OrderItem extra 0 readonly_fields (dish, quantity, price) admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display (id, table_no, total_price, status, created_at) list_filter (status,) inlines [OrderItemInline] admin.register(Dish) class DishAdmin(admin.ModelAdmin): list_display (name, category, price, is_active) list_editable (price, is_active)逻辑说明OrderItemInline让订单编辑页直接内嵌明细不用跳转。list_editable让菜品列表页可以直接改价格和上下架状态省去点进详情再保存的步骤。list_filter按订单状态筛选处理待支付订单时很实用。参数说明extra 0表示内嵌表单默认不显示空行避免页面太长。readonly_fields把明细里的菜品和价格设为只读防止误改历史数据。另一个提效点是缓存。菜品列表变化不频繁可以用 django 的缓存框架把dish_list的结果缓存 60 秒减少数据库压力。from django.core.cache import cache def dish_list(request): data cache.get(dish_list) if data is None: # ... 原来的查询逻辑 ... cache.set(dish_list, data, 60) return JsonResponse({code: 0, data: data})逻辑说明先查缓存命中就直接返回没命中才查库并写缓存。60 秒的过期时间是个平衡点既能挡住高频刷新又不至于菜品更新后太久不生效。注意后台改了菜品要手动cache.delete(dish_list)否则会出现改了价格前端还显示旧价的玄学问题。参数说明缓存后端默认是本地内存毕设够用。如果要多进程部署得换成 Redis配置CACHES指向 Redis 地址。最后说个我自己的习惯这套项目跑通之后别急着加功能先把settings.py里的DEBUG在演示前改成False并配好ALLOWED_HOSTS再跑一遍全流程。我见过太多人本地跑得好好的一关 DEBUG 就白屏原因是静态文件没配。提前踩这个坑比答辩当天翻车强。希望帮到你。本文还有配套的精品资源点击获取