首页 / 资讯中心 / 文章详情

Python Web简易订单系统毕设实战:Django/Flask选型与订单状态流转

Python Web简易订单系统毕设实战:Django/Flask选型与订单状态流转 ★ FEATURED ARTICLE
简介这份资源是面向计算机专业学生与Python Web初学者的毕业设计完整源码包主题为基于Python Web框架开发的简易订单系统可用于课程设计参考、毕设选题复现或Web开发入门练手。压缩包共95个文件约216KB以31个py源码文件为核心配合15个html模板、12个js脚本、7个css样式及若干png、gif图片资源另含conf配置、md说明文档、sql建库脚本与sh部署脚本覆盖后端逻辑、前端页面、数据库与部署配置等模块。目录中可见dao、bean、web分层结构及nginx、supervisor等部署配置便于理解MVC分层与工程组织方式。目前已有128人学习下载。通过研读源码读者可掌握用户认证、订单创建、路由处理、模板渲染与数据库交互等关键实现并借鉴代码组织、文档编写与测试策略等软件工程实践适合作为Python Web开发技能的实战案例。1. 从一份毕设压缩包说起简易订单系统到底该做成什么样很多同学拿到「基于 Python Web 开发的简易订单系统」这个题目时第一反应是打开搜索引擎找一份免费 Python 源码大全下载一个跑起来能登录、能下单的 demo然后改改界面就交差。但真正做过答辩项目的人都知道导师问的从来不是「你能不能跑起来」而是「订单状态怎么流转」「库存扣减和订单创建是不是一个事务」「并发下单会不会超卖」。这个项目本质上是一个用 Python Web 框架搭建的、覆盖「用户下单 → 订单生成 → 状态管理 → 后台查看」这条最小闭环的信息系统技术栈通常落在 Django 或 Flask 上数据库用 MySQL 或 SQLite前端可以是模板渲染也可以是前后端分离。它适合正在做课程设计或毕业设计的同学也适合想用一个完整小项目把 Python Web 开发流程走一遍的入门者。核心难点不在页面多好看而在订单这个业务对象的状态机和数据一致性。2. 技术选型Django 还是 FlaskSQLite 还是 MySQL2.1 两个框架在这个项目里的真实差别选框架这件事网上吵了十几年但落到「简易订单系统」这个具体场景判断标准其实很窄你需要不需要现成的后台管理、需不需要 ORM 自带迁移、需不需要用户认证开箱即用。Django 自带 admin 后台、auth 认证、ORM 和 migration订单系统的「商品管理」「订单列表」「用户管理」几乎不用写多少代码就能在 admin 里看到。对于毕设来说这意味着你能把精力放在订单状态流转和库存逻辑上而不是花三天写一个登录页面。Django 的django-admin startproject一条命令就能起一个结构完整的工程settings 里改几行数据库配置就能连 MySQL。Flask 更轻路由和视图函数直白适合你想自己控制每一层、或者题目要求「不使用重型框架」。但 Flask 没有自带 ORM 和后台你需要自己选 SQLAlchemy、自己写登录装饰器、自己搭管理页面。对于「简易」订单系统这些额外工作量不一定划算除非你的答辩老师明确要求体现「自己实现」的过程。我一般的建议是如果时间紧、想稳选 Django如果想展示对 Web 请求生命周期的理解选 Flask SQLAlchemy。两者都能做出合格的毕设区别在于你愿意把时间花在业务逻辑还是基础设施上。2.2 数据库选择与最小环境搭建毕设阶段用 SQLite 完全够用它零配置、单文件、方便打包进 zip 提交。但如果你想让项目看起来更「企业级」或者导师明确要求 MySQL那就装 MySQL 8.x建一个库改 Django 的DATABASES配置即可。下面是以 Django MySQL 为例的最小环境搭建步骤。# 创建虚拟环境避免污染系统 Python python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境macOS/Linux source venv/bin/activate # 安装 Django 和 MySQL 驱动 pip install django mysqlclient # 创建项目项目名用 order_system django-admin startproject order_system # 进入项目目录创建订单应用 cd order_system python manage.py startapp orders这几条命令做完你会得到一个标准 Django 工程order_system/settings.py管配置order_system/urls.py管总路由orders/是你写业务的地方。mysqlclient是 MySQL 的 Python 驱动比pymysql性能好但 Windows 上安装可能需要编译工具装不上就换pymysql在__init__.py里加两行pymysql.install_as_MySQLdb()也能跑。数据库配置在settings.py里改# order_system/settings.py DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: order_db, # 提前在 MySQL 里建好的库名 USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, # 支持中文和 emoji }, } }NAME对应的库要提前用CREATE DATABASE order_db CHARACTER SET utf8mb4;建好否则 migrate 会报错。utf8mb4而不是utf8是因为 MySQL 的utf8实际只支持三字节存中文没问题但存某些特殊字符会翻车这个坑很多人踩过。2.3 订单系统的数据模型设计模型是订单系统的骨架设计不好后面全是后悔药。最小可用的模型有四个商品、订单、订单明细、用户Django 自带 User 可直接用。核心字段和关系如下表。模型关键字段说明Productname, price, stock, created_at商品名、单价、库存、创建时间Orderuser, order_no, status, total_amount, created_at订单号唯一状态用枚举OrderItemorder, product, quantity, price下单时的单价要快照不能只关联商品UserDjango 内置直接用 auth.User订单状态建议用整数枚举而不是字符串查询和索引都更高效# orders/models.py from django.db import models from django.contrib.auth.models import User class Product(models.Model): name models.CharField(max_length100) price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.name class Order(models.Model): STATUS_CHOICES ( (0, 待支付), (1, 已支付), (2, 已发货), (3, 已完成), (4, 已取消), ) user models.ForeignKey(User, on_deletemodels.CASCADE) order_no models.CharField(max_length32, uniqueTrue) status models.IntegerField(choicesSTATUS_CHOICES, default0) total_amount models.DecimalField(max_digits10, decimal_places2, default0) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.order_no class OrderItem(models.Model): order models.ForeignKey(Order, related_nameitems, on_deletemodels.CASCADE) product models.ForeignKey(Product, on_deletemodels.CASCADE) quantity models.IntegerField() price models.DecimalField(max_digits10, decimal_places2) # 下单时单价快照OrderItem.price必须单独存一份不能只靠product.price现查。因为商品价格会变订单一旦生成历史价格就必须冻结否则用户三个月后看订单金额对不上这是订单系统的基本要求。order_no用uniqueTrue保证唯一生成规则可以用时间戳加随机数后面会讲。3. 下单流程实现从加购物车到订单落库3.1 下单接口的完整逻辑与事务边界下单是整个系统最容易出问题的地方。表面上是「用户点一下按钮」背后要做的事包括校验库存、扣减库存、生成订单号、创建订单记录、创建订单明细、计算总价。这些操作必须在一个数据库事务里完成否则任何一步失败都会留下脏数据。下面是一个基于 Django 视图的下单函数用transaction.atomic包住整个流程# orders/views.py import time import random from decimal import Decimal from django.db import transaction from django.http import JsonResponse from django.contrib.auth.decorators import login_required from .models import Product, Order, OrderItem def generate_order_no(): 订单号时间戳 随机数保证唯一性 return time.strftime(%Y%m%d%H%M%S) str(random.randint(1000, 9999)) login_required def create_order(request): if request.method ! POST: return JsonResponse({code: 1, msg: 请求方式错误}) # 假设前端传来 [{product_id, quantity}, ...] items request.POST.getlist(items) if not items: return JsonResponse({code: 1, msg: 购物车为空}) try: with transaction.atomic(): total Decimal(0) order Order.objects.create( userrequest.user, order_nogenerate_order_no(), status0, total_amount0, ) for item in items: product_id, quantity item[product_id], int(item[quantity]) # select_for_update 锁行防止并发超卖 product Product.objects.select_for_update().get(idproduct_id) if product.stock quantity: raise ValueError(f{product.name} 库存不足) product.stock - quantity product.save() OrderItem.objects.create( orderorder, productproduct, quantityquantity, priceproduct.price, ) total product.price * quantity order.total_amount total order.save() except ValueError as e: return JsonResponse({code: 1, msg: str(e)}) except Exception: return JsonResponse({code: 1, msg: 下单失败请重试}) return JsonResponse({code: 0, msg: 下单成功, order_no: order.order_no})这段代码有几个关键点。transaction.atomic()保证所有数据库操作要么全成功要么全回滚不会出现「库存扣了但订单没生成」的情况。select_for_update()是行级锁在并发场景下锁住商品行防止两个请求同时读到相同库存然后都扣减导致超卖。generate_order_no用时间戳加随机数简单够用但如果你的系统每秒订单量很大随机数可能碰撞那就换成 UUID 或者数据库自增 ID 拼接。参数方面quantity必须做正整数校验负数或零要直接拒绝否则用户传-1就能把库存加上去。product_id要校验存在性get不到会抛DoesNotExist被外层except Exception捕获后返回通用错误但更好的做法是单独捕获并返回明确提示。3.2 订单状态流转与后台管理订单创建后是「待支付」状态后续的支付、发货、完成、取消都是状态迁移。状态迁移要有明确的规则不能随便改。常见做法是在模型上加方法或者在视图里判断当前状态是否允许目标操作。# orders/models.py 中给 Order 加方法 class Order(models.Model): # ... 字段省略 ... def can_transition_to(self, target_status): 定义合法的状态迁移 allowed { 0: [1, 4], # 待支付 - 已支付 / 已取消 1: [2, 4], # 已支付 - 已发货 / 已取消需退款 2: [3], # 已发货 - 已完成 3: [], # 已完成不可再变 4: [], # 已取消不可再变 } return target_status in allowed.get(self.status, [])后台管理直接用 Django admin注册模型后就能在浏览器里查看和修改订单。但要注意admin 里直接改status字段会绕过上面的状态校验所以更严谨的做法是自定义 admin action或者在save方法里做校验。毕设阶段如果导师不深究admin 直接改也能接受但答辩时被问到「状态能不能乱改」就尴尬了。# orders/admin.py from django.contrib import admin from .models import Product, Order, OrderItem class OrderItemInline(admin.TabularInline): model OrderItem extra 0 admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display (order_no, user, status, total_amount, created_at) list_filter (status,) search_fields (order_no,) inlines [OrderItemInline] admin.site.register(Product)list_display控制列表页显示哪些列list_filter提供右侧筛选search_fields支持按订单号搜索inlines让订单明细直接嵌在订单编辑页里。这些配置不写也能用但写了之后后台体验完全不一样答辩演示时也更好看。3.3 前端页面与接口联调如果做模板渲染Django 的模板系统足够用商品列表页、购物车页、订单确认页各写一个 template用{% for %}循环渲染数据。如果做前后端分离后端只返回 JSON前端用 Vue 或 React 调接口。毕设阶段模板渲染更快出效果因为不用处理跨域和 token 传递。联调时最常见的翻车是 CSRF。Django 默认开启 CSRF 保护POST 请求必须带csrfmiddlewaretoken。模板里加{% csrf_token %}就行但如果是 AJAX 请求要从 cookie 里取csrftoken放到请求头X-CSRFToken里。很多同学调试时直接csrf_exempt关掉保护这在毕设里能跑但答辩时被问到安全性就不好解释。4. 避坑与排查订单系统最容易翻车的五个地方4.1 并发下单导致库存变负数现象压测或多人同时下单时商品库存出现负数或者两个订单都扣了同一件库存。原因没有加锁两个请求同时读到stock1各自判断「库存足够」然后都执行stock - 1最终库存变成 -1。解决用select_for_update()在事务内锁行或者用数据库层面的乐观锁版本号字段。Django 的F()表达式也能做原子更新Product.objects.filter(idpid, stock__gteqty).update(stockF(stock) - qty)根据返回的受影响行数判断是否扣减成功。4.2 订单金额与明细对不上现象订单表里的total_amount和订单明细里各项相加的结果不一致。原因计算总价时用了浮点数或者先创建订单再逐条加明细时中间出错回滚不完整。解决金额一律用Decimal不要用float。总价在事务内最后统一计算并写回订单不要分多次更新。如果用了F()表达式更新记得refresh_from_db()再读。4.3 订单号重复现象极短时间内生成的两个订单号一样数据库报唯一约束冲突。原因订单号生成规则太简单比如只用时间戳到秒同一秒内多单就碰撞。解决时间戳精确到毫秒或微秒再加随机数或用户 ID 后缀。更稳妥的是用uuid.uuid4().hex截取碰撞概率极低。4.4 商品删除后订单明细查不到商品现象后台删除了某个商品历史订单里的明细显示「商品不存在」或直接报错。原因OrderItem.product用了on_deletemodels.CASCADE商品删除时明细也被级联删除了。解决改成on_deletemodels.SET_NULL并允许nullTrue或者用PROTECT禁止删除有订单关联的商品。订单明细里已经存了price快照商品名也可以冗余存一份这样即使商品被删历史订单依然完整。4.5 时区问题导致订单时间差 8 小时现象订单创建时间比实际时间少 8 小时或者后台筛选按日期筛不到当天的单。原因Django 默认USE_TZTrue用 UTC 存储TIME_ZONE设成UTC而你在东八区。解决settings.py里设TIME_ZONE Asia/ShanghaiUSE_TZ True保持不变。模板里显示时间用{{ order.created_at|date:Y-m-d H:i }}Django 会自动转成当前时区。如果存的是 naive datetime那就把USE_TZ设为False但这不是推荐做法。5. 让毕设更稳的几个进阶技巧5.1 用 Django 信号做订单日志订单状态每次变更都值得记一条日志方便排查和答辩演示。Django 的post_save信号可以在订单保存后自动写日志不用在每个视图里手动加。# orders/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from .models import Order receiver(post_save, senderOrder) def log_order_status(sender, instance, created, **kwargs): if created: print(f[订单创建] {instance.order_no} 金额 {instance.total_amount}) else: print(f[订单更新] {instance.order_no} 状态 {instance.status})然后在orders/apps.py的ready()方法里import orders.signals注册信号。生产环境当然不会用print而是写文件或数据库但毕设演示时控制台输出足够直观。5.2 用管理命令批量生成测试数据答辩前需要造一批订单数据来演示列表和筛选手动点太慢。写一个 Django management command一条命令生成几百条订单。# orders/management/commands/seed_orders.py import random from django.core.management.base import BaseCommand from django.contrib.auth.models import User from orders.models import Product, Order, OrderItem from orders.views import generate_order_no class Command(BaseCommand): help 生成测试订单数据 def handle(self, *args, **options): user User.objects.first() products list(Product.objects.all()) if not user or not products: self.stdout.write(请先创建用户和商品) return for _ in range(100): order Order.objects.create( useruser, order_nogenerate_order_no(), statusrandom.choice([0, 1, 2, 3]), total_amount0, ) total 0 for product in random.sample(products, random.randint(1, 3)): qty random.randint(1, 3) OrderItem.objects.create( orderorder, productproduct, quantityqty, priceproduct.price, ) total product.price * qty order.total_amount total order.save() self.stdout.write(self.style.SUCCESS(已生成 100 条测试订单))放到orders/management/commands/目录下运行python manage.py seed_orders即可。这个命令在答辩前跑一次后台列表立刻有数据可看比空荡荡的页面有说服力。5.3 部署与演示的注意事项毕设演示通常在本机跑python manage.py runserver但要注意DEBUGTrue时 Django 会暴露详细错误页答辩时如果某个页面报错满屏的配置信息不太好看。演示前把DEBUG设为False并配置ALLOWED_HOSTS [127.0.0.1, localhost]同时设置STATIC_ROOT并跑python manage.py collectstatic否则 admin 的样式会丢失。如果导师要求部署到服务器用gunicorn加nginx是常见组合但毕设阶段用runserver加--insecure也能应付静态文件。数据库记得提前导入测试数据别到演示时现场建商品。我自己的习惯是答辩前一晚把整个流程从注册、登录、加购、下单、后台改状态走一遍每一步截图存好。因为现场紧张时容易点错有截图兜底。这个项目本身不复杂真正拉开差距的是细节——订单号不重复、金额不飘、并发不超卖、时间不错位。把这些做到位一个「简易」订单系统就足够扎实了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站