简介面向毕业设计与 Python 桌面开发学习的工厂管理系统完整工程基于 Tkinter 构建图形界面覆盖库存管理、订单处理、生产调度、员工管理、数据库持久化与用户权限控制等核心业务模块适合需要完成课程设计或想要系统掌握 Python GUI 开发流程的学生参考。压缩包内共 4456 个文件约 36.89MB以 py 源码、pyc 编译文件、dat 数据文件为主体另含 txt 文档、pyd 动态库、wheel 依赖包及安装元数据并配有测试与运维文档便于理解项目部署及调试流程。目前已有 92 人学习下载整体结构完整、模块划分清晰既可作为独立完成的毕设项目蓝本也能直接复用其中界面交互、数据库操作、异常处理等代码片段对提升工程实践能力和答辩准备都很有帮助。1. Python工厂管理系统毕设选题到底值不值得做机会全在这份文档里每年计算机毕设选题工厂管理系统都是常青树但大多数学生做的只是「进销存换皮」登录、增删改查、一个统计图答辩时老师问一句“工单怎么流转”就卡壳。而这个标题里多了两个关键词——python和有文档分量完全不同。Python 做后端管理系统开发速度快、生态成熟Django 自带的 Admin 后台能在三天内先跑出原型而「有文档」恰恰是毕设最稀缺的东西绝大多数人代码写完才补文档甚至直接找代写最后连自己系统有哪些表都说不清。这篇笔记要解决的就是三件事这套系统用 Python 到底该怎么选型、核心模块怎么落代码、文档要怎么写到能答辩。适合正在做或准备做这个方向的学生也适合想快速交付这类外包项目的开发者。我会按我实际做过的方式讲不画大饼每一段命令和代码都对应真实可跑的方案。全文没有一句是某个开源项目的官方说明全是我自己习惯的做法你照着改参数就能用。2. 技术选型与数据库设计先定框架再写代码毕设才不返工2.1 Django vs Flask vs FastAPI毕设管理系统怎么选工厂管理系统这类业务系统核心特征是实体多、关系复杂、权限要求明确、需要后台管理界面。三个主流 Python 框架里我的选择是 Django原因很直接——你不需要重复造轮子。Django 自带 ORM、Admin、迁移工具、认证系统这些正是管理系统最花时间的部分。Flask 灵活但用户管理、数据库迁移、表单验证都要自己拼拼完的代码量比 Django 多出一倍而且拼出来的东西大概率有安全漏洞。FastAPI 性能好但它是为 API 场景设计的模板渲染、Admin 后台这些它不管毕设做到中期你会发现什么都得自己补。有一个常见的误选是因为毕设题目里带 springbootvue 的热词就去学 Java或者因为想省事就选 SSM。如果你的技术栈已经定了 Python那就别换赛道。工厂管理系统本质上是一个「数据录入→流程审批→报表统计」的应用Django 的 MTV 架构和这个业务模型天然匹配Model 对应数据库表Template 对应页面View 对应业务逻辑。Admin 后台还能直接当简单的数据维护界面省掉一大半前端工作量。提示如果你的毕设要求里明确写了必须用前后端分离那再考虑 Django REST FrameworkDRF它和 Django 共用 Models不需要换框架。版本选择上我建议 Django 4.x 系列不要追 Django 5 的最新特性稳定性和第三方库兼容性才是关键。Python 解释器用 3.10 或 3.11别用 3.12 以下的最新版——部分第三方库比如某些报表导出库还没跟上装的时候会报错这不是你代码的问题是版本生态的时序差。2.2 建表顺序与核心字段把物料清单和生产工单的关系先画清楚工厂管理系统最核心的不是用户表而是物料清单BOM和生产工单Work Order。很多毕设翻车就翻在这里上来先写用户登录写完用户写产品写完产品写订单最后发现订单和库存对不上。我的经验是先画关系图再建表顺序是部门/用户 → 物料 → 产品BOM → 生产工单 → 库存流水 → 工序报工。先动手把 models.py 的骨架写出来第一版不需要所有字段齐全但要保证外键关系方向正确。下面是我常用的核心模型代码from django.db import models from django.contrib.auth.models import User # 物料表原材料、半成品、成品统一用一张表用类型区分 class Material(models.Model): code models.CharField(物料编码, max_length50, uniqueTrue) # 编码全局唯一 name models.CharField(物料名称, max_length100) spec models.CharField(规格型号, max_length100, blankTrue) material_type models.SmallIntegerField(类型, choices((1,原材料),(2,半成品),(3,成品)), default1) unit models.CharField(单位, max_length10, default件) safety_stock models.FloatField(安全库存, default0) # 低于此值告警 price models.DecimalField(参考单价, max_digits10, decimal_places2, default0) create_time models.DateTimeField(auto_now_addTrue) class Meta: db_table base_material ordering [code] def __str__(self): return f{self.code}-{self.name} # 生产工单一个工单对应一个产品、一个批量数量、一套审批流转状态 class WorkOrder(models.Model): order_no models.CharField(工单号, max_length30, uniqueTrue) # 如 WO20240611001 product models.ForeignKey(Material, on_deletemodels.PROTECT, related_namework_orders, verbose_name生产产品) quantity models.FloatField(计划数量, default1) completed_qty models.FloatField(完工数量, default0) status models.SmallIntegerField(状态, choices( (0,草稿), (1,已下达), (2,生产中), (3,已完工), (4,已取消) ), default0) plan_start models.DateField(计划开始, nullTrue, blankTrue) plan_end models.DateField(计划结束, nullTrue, blankTrue) owner models.ForeignKey(User, on_deletemodels.SET_NULL, nullTrue, related_namework_orders, verbose_name负责人) create_time models.DateTimeField(auto_now_addTrue) update_time models.DateTimeField(auto_nowTrue) class Meta: db_table produce_work_order ordering [-id] # 新工单排前面 def __str__(self): return self.order_no这段代码里有两个关键设计。第一个是 Material 表不分原材料和成品两张表而是用 material_type 字段区分。这是工厂管理系统的典型做法因为原材料和成品在「编码、规格、库存、价格」这些属性上完全一致分表反而让 BOM 关联变得复杂。第二个是 WorkOrder 的 status 用整数而不是字符串这是给后续状态流转做准备的——流程判断用数字判断比用中文判断更可靠而且 Admin 后台能直接显示中文标签。外键的 on_delete 参数要特别说明WorkOrder 关联的 product 用的是 PROTECT意思是如果有工单引用某个物料那么这个物料不允许删掉。这是一个防呆设计避免学生误删数据导致库存和工单对不上。而 owner 用 SET_NULL 是因为用户可能被删除但工单历史不能丢。这两个参数是答辩时老师最爱问的细节能答上来就是加分项。2.3 库存表为什么要设计成流水账库存模块是另一个区分「应付了事」和「真懂业务」的分水岭。初级做法是建一张 inventory 表存当前库存数量每次操作直接改数量。这能用但一查历史就全瞎了说不清这批库存是哪个工单领的、哪个时间点入库的、为什么账面数和实际数对不上。我的做法是库存余额表只存当前数值同时建一张库存流水表记录每一次变化的来龙去脉。余额表用来查询和展示流水表用来追溯和审计。看代码class Inventory(models.Model): material models.OneToOneField(Material, on_deletemodels.CASCADE, related_nameinventory, verbose_name物料) quantity models.FloatField(当前库存, default0) # 这个字段是冗余的但查询快 update_time models.DateTimeField(auto_nowTrue) class Meta: db_table base_inventory class InventoryLog(models.Model): material models.ForeignKey(Material, on_deletemodels.CASCADE, related_namestock_logs, verbose_name物料) change_qty models.FloatField(变动数量) # 正数入库负数出库 after_qty models.FloatField(变动后库存) biz_type models.CharField(业务类型, max_length20, choices( (purchase,采购入库), (material_out,生产领料), (product_in,成品入库), (check,盘点调整) ), defaultmaterial_out) order models.ForeignKey(WorkOrder, on_deletemodels.SET_NULL, nullTrue, blankTrue, related_namestock_logs, verbose_name关联工单) note models.CharField(备注, max_length200, blankTrue) operator models.ForeignKey(User, on_deletemodels.SET_NULL, nullTrue, verbose_name操作人) create_time models.DateTimeField(auto_now_addTrue) class Meta: db_table base_inventory_log ordering [-id]这个设计的核心是 InventoryLog每次操作都记一条流水Inventory 表的 quantity 是冗余字段每次操作后刷新。为什么冗余一个余额字段因为工单列表、产品列表、看板都要显示当前库存如果每次都去 SUM 流水表数据量大了之后 SQL 会越来越慢。这是典型的空间换时间。答辩时如果你的数据库设计能说清楚「为什么有冗余字段」老师会认为你有真实项目经验而不是只会照抄。注意这里 OneToOneField 用的 CASCADE意味着物料删了库存记录也删。但前面 Material 被 WorkOrder 用 PROTECT 保护所以实际不会被删——两层保护叠加才是完整防线。3. 产线核心功能实现生产工单、库存扣减与报表统计3.1 生产工单模块models.py 这样写审批流转不乱跳很多毕设的生产工单只是一个带着状态字段的 CRUD点一个按钮改一次状态。这在演示时能过但答辩老师问「如果工单已经在生产能不能直接取消」就露馅了。状态流转必须有业务规则不能任意跳转。我习惯的做法是把状态变更封装成一个独立的函数不直接在视图里改 status。这个函数写在 models.py 里视图和测试都调用它保证状态流转只按规则走class WorkOrder(models.Model): # ... 上面已有字段这里补充流转函数 ... def set_status(self, new_status, userNone): 工单状态流转的唯一入口非法跳转会抛异常 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(new_status)}) # 如果从已下达变成生产中必须已经领过料 if new_status 2: if self.stock_logs.filter(biz_typematerial_out, orderself).count() 0: raise ValueError(未领料不能开始生产) self.status new_status self.save(update_fields[status, update_time]) # 记录操作日志答辩时有话可讲 if user: WorkOrderLog.objects.create(orderself, operatoruser, from_statusself.get_status_display(), to_statusnew_status)这段代码的关键是allowed字典它定义了状态机。草稿可以下达和取消已下达可以开工和取消生产中只能完工。最后一个 if 判断领料记录是为了保证业务逻辑闭环——没领料就开始生产在工厂里是重大事故。每次变更都写一条 WorkOrderLog 流水这个流水表在答辩时展示出来是极强的加分项因为绝大多数毕设没有操作审计的概念。3.2 库存扣减与领料事务与锁的边界工单下达之后领料出库是第一个真正的并发场景。如果用最朴素的写法——先查库存、判断够不够、再减库存——在多人同时操作时一定会超卖。Django 的 ORM 在常规查询下不会自动加锁你需要用select_for_update()手动锁行。下面是我写的领料函数from django.db import transaction transaction.atomic def material_out(order, material, qty, operator): 生产领料扣库存 写流水同一事务内完成 # 锁住物料对应的库存行避免并发扣减 inventory Inventory.objects.select_for_update().get(materialmaterial) if inventory.quantity qty: raise ValueError(f{material.name} 库存不足当前 {inventory.quantity}{material.unit}) inventory.quantity - qty inventory.save(update_fields[quantity, update_time]) InventoryLog.objects.create( materialmaterial, change_qty-qty, after_qtyinventory.quantity, biz_typematerial_out, orderorder, operatoroperator, ) return inventory.quantitytransaction.atomic保证扣库存和写流水要么都成功要么都失败。select_for_update()是数据库层的行级锁在事务提交前其他事务无法修改这一行。这里要记住一个隐患锁必须和事务在同一连接上才生效也就是说这个函数不能拆开其中一个操作放到另一段代码里执行。很多新手把save()和create()写进两个视图函数里导致事务边界失效这是并发扣减变成负数的头号原因。如果不想背这个复杂度有个省心的替换方案直接用一个 update 条件语句原子扣减updated Inventory.objects.filter(materialmaterial, quantity__gteqty).update(quantityF(quantity) - qty) if updated 0: raise ValueError(库存不足或物料不存在)这种写法不需要手动锁quantity__gteqty条件让数据库在 update 时判断天然防止超卖。代价是不能拿到扣减后的精确库存值需要再查一次但对毕设系统来说这个取舍完全值得。两个方案你都写在代码注释里答辩时主动讲区别是从「会写」到「懂原理」的明显转折。3.3 报表统计用 ORM 聚合把 dashboard 数据一次查出来工厂管理系统的报表高频的是每日产量、工单完工率、物料库存预警、领料趋势。初级写法是循环查数据库一个月数据量几百条时没感觉但答辩上老师会问「这条 SQL 执行了几次」。聚合查询用 ORM 的annotate和values一条查询把整个统计结果拿到手。以「每种产品的累计完工数量」为例from django.db.models import Sum, Count, Q from datetime import date, timedelta def production_summary(days7): 最近 N 天各产品完工数量统计 start_date date.today() - timedelta(daysdays) result ( WorkOrder.objects .filter(status3, update_time__date__gtestart_date) # 只算已完工的工单 .values(product__code, product__name) # 按产品分组 .annotate( total_qtySum(completed_qty), order_countCount(id), ) .order_by(-total_qty) ) return list(result)这段代码最终生成的 SQL 大概是SELECT product_id, SUM(completed_qty), COUNT(id) FROM produce_work_order WHERE status3 GROUP BY product_id数据库只跑一次。注意values()和annotate()的顺序一定是先values分组再annotate聚合写反了结果会完全不同这是 Django ORM 最容易踩的语法坑。报表页面我建议不要用复杂图表库直接用 Django Admin 或者简单的 HTML ECharts CDN 就够。毕设论文里更重要的是数据表的字段设计和查询逻辑前端图表的炫酷程度在答辩评分占比很低别把时间花在这里。把时间省下来写文档收益远大于画图。4. 环境配置与文档交付从代码到能跑的完整流程4.1 本地开发环境VSCode/PyCharm 跑通 Django 的最小命令很多毕设卡在第一步——环境装不上。这个环节新手容易犯的错误是同时装多个 Python 版本导致解释器混乱。我的做法是全程用虚拟环境一个项目一套环境绝不混装。先说 Python 本身。Windows 上建议从官网下载安装包勾选“Add Python to PATH”再安装。安装完用python --version验证。macOS 和 Linux 建议用apt或brew不展开。VSCode 和 PyCharm 二选一我两个都用过VSCode 轻量但 Django 项目调试要自己配 launch.jsonPyCharm 社区版对 Django 支持开箱即用适合不想折腾环境的学生。建议选 PyCharm 社区版省下配置时间写代码。接着是关键命令序列# 建项目目录并进入 mkdir factory_manage cd factory_manage # 创建虚拟环境一定要在项目目录内做 python -m venv venv # 激活虚拟环境Windows 与 Linux/macOS 命令不同别记混 venv\Scripts\activate.bat # Windows CMD source venv/bin/activate # Linux/macOS # 安装 Django建议锁定版本4.2 稳定且有安全补丁 pip install django4.2.* pip install mysqlclient # 如果用 MySQL 需要这个库 # 新建项目manage.py 所在目录是项目根 django-admin startproject config . # 新建一个业务 App工厂系统的核心放这里 python manage.py startapp mes # 第一次迁移生成内置的用户表 python manage.py migrate # 创建管理员账号用于登录 Admin 后台 python manage.py createsuperuser # 启动开发服务器默认 8000 端口 python manage.py runserver第 2 行创建虚拟环境的venv名字是可以自定义的但建议固定叫venv因为后面写部署文档时所有人看到这个目录名都认识。第 5 行pip install django4.2.*里的通配符是锁定大版本装最新小版本这是一个让依赖保持可升级又不会突然大变的折中方案。第 8 行django-admin startproject config .注意最后的点不能丢它表示在当前目录创建配置文件。忘了点的话会多套一层目录到处都要改路径返工率极高这是新手常见翻车点。运行runserver后浏览器访问http://127.0.0.1:8000看到火箭页面说明环境已经通了。如果你看到的不是火箭而是报错九成是端口被占用换一个端口python manage.py runserver 8001。4.2 settings.py 必改的几个参数数据库、时区、静态文件Django 默认是 SQLite毕设管理系统如果数据量不大其实够用但工厂场景我更建议换成 MySQL理由有两点一是 MySQL 更接近真实生产环境论文里写部署方案时有说服力二是 SQLite 对select_for_update()的支持和并发控制不如 MySQL前面说的行锁在 SQLite 上是弱化的。当然如果你本机没装 MySQL坚持 SQLite 也能答辩只是要把这个边界说清楚。settings.py 里必改的是这段# 时区与语言国内项目一定要改否则时间差 8 小时 LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ True # 数据库存储 UTC显示时转本地 # 数据库切换把引擎改成 MySQL 并填账号密码 DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: factory_db, USER: root, PASSWORD: 你的密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } } # 静态文件与媒体文件上传的产品图、导入的 Excel 都走这里 MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media STATIC_URL static/ STATICFILES_DIRS [BASE_DIR / static]USE_TZ True是新手最费解的参数。它的意思是数据库里存 UTC 时间页面上展示时按Asia/Shanghai转成本地时间。如果你把它设为 FalseDjango 会直接存本地时间在迁移和数据比较时容易出偏差。建议保持 True只在settings.py里改TIME_ZONE。charset: utf8mb4是另一个必改项MySQL 的默认 utf8 并不是完整的 UTF-8存储生僻字或 emoji 会报错utf8mb4 才是完整实现。很多系统上线后中文乱码都是这里没设。改完配置后执行python manage.py migrate让新配置生效如果提示连接不上数据库去确认 MySQL 服务有没有启动。4.3 文档体系需求文档、数据库设计文档、部署文档怎么写这个标题里的「有文档」我理解成三份东西需求文档、数据库设计文档、部署说明。这三份正好对应论文的三大章节也是答辩提问的重灾区。需求文档不需要写长篇大论用表格列功能模块就够了。我习惯在项目根目录建一个docs文件夹里面按模块写。每个模块写清楚四件事输入是什么、操作是什么、校验规则是什么、输出是什么。比如工单模块「输入产品、数量、计划起止日期操作下达/开工/完工/取消校验未领料不能开工、产量不能超过计划量输出工单状态变更记录、库存流水」。这种写法老师在论文里一眼就能看到结构答辩前自己也用得上。数据库设计文档用表格列出每张表的核心字段。可以用 Django 的manage.py inspectdb反查但更直接的方法是打开models.py逐个抄。关键是画出表关系说明WorkOrder 和 InventoryLog 是一对多Material 和 Inventory 是一对一这些关系描述比字段清单更重要。部署文档按步骤写从python -m venv到runserver把 4.1 节的命令原样复制进去。注意注明每个命令在哪个目录执行以及执行完如何验证。写文档的时候想象读者是一台新电脑所有环境从零开始这样的文档才是真的「有文档」。没有验证步骤的部署文档答辩现场八成都翻车。提示写部署文档时把虚拟环境激活命令放在最前面并加粗提示这是别人复现你系统时最容易出错的地方。5. 常见问题和避坑手册毕设管理系统最容易断送答辩的五个翻车点5.1 外键循环引用导致迁移失败现象执行python manage.py makemigrations时报ValueError: Unable to serialize database access或者循环依赖错误。WorkOrder 引用了 MaterialMaterial 又想引用 WorkOrder两个模型互相 import迁移文件无法生成。原因在两个 models.py 文件里互相 import 对方形成 A → B → A 的循环引用。Django 的迁移系统无法确定表创建的先后顺序。解决用字符串引用代替直接 import。在models.ForeignKey(mes.WorkOrder)中把类名写成字符串Django 会延迟解析打破循环。同时保证父表先创建比如 Material 不依赖 WorkOrderWorkOrder 依赖 Material这个方向不能反。如果已经建了错误的外键删掉迁移文件重新 makemigrations别试图手动改依赖关系。5.2 中文乱码与 CSV 导出乱码现象Admin 页面中文正常但 Excel 导出的 CSV 用 Excel 打开全是乱码或者 MySQL 里中文变成问号。原因CSV 文件默认编码是系统区域编码Windows 下 Excel 默认用 GBK 打开 UTF-8 文件导致中文无法识别。MySQL 乱码则是因为表或连接字符集没设成 utf8mb4。解决写 CSV 导出时在文件开头加 BOMimport csv def export_csv_response(rows, headers, filename): response HttpResponse(content_typetext/csv; charsetutf-8-sig) response[Content-Disposition] fattachment; filename{filename}.csv writer csv.writer(response) writer.writerow(headers) writer.writerows(rows) return responsecharsetutf-8-sig是带 BOM 的 UTF-8Excel 能自动识别。MySQL 乱码的治本方法是创建数据库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci同时 settings.py 里OPTIONS的 charset 保持 utf8mb4。两个地方不一致就一定会乱。5.3 工单状态堆积在「已下达」无法进入生产中现象演示时点击「开始生产」按钮系统提示错误但明明已经做了领料操作。原因工单状态流转函数set_status(2)里检查了是否已经领料但领料时material_out函数里创建 InventoryLog 时没有把order参数传进去导致流水表里的order字段是 NULLself.stock_logs.filter(biz_typematerial_out, orderself)查不到记录误判为未领料。解决领料操作里必须传orderwork_order参数并且在写函数时先跑一遍单元测试验证关联关系。这个坑的典型特征是你的业务逻辑没有任何报错但状态就是走不下去。排查方法很简单进 Django shell 里查WorkOrder.objects.first().stock_logs.all()看看流水记录的 order 字段是否为空。5.4 并发领料把库存扣成负数现象两个用户几乎同时点击领料库存显示还有 10 件但两笔都成功了库存变成负数。原因两条请求同时读到库存 10各自判断 10 领料数量然后各自扣减后一个覆盖前一个。没有锁或者事务边界不对。解决把领料逻辑放进transaction.atomic并用select_for_update()锁行或者直接用条件更新filter(quantity__gteqty).update(quantityF(quantity) - qty)。这里的关键是「判断库存是否足够」和「扣减库存」必须在一个原子操作内完成拆分到两个函数就堵不住并发。5.5 文档和代码对不上答辩前一晚的翻车现象论文里写了「系统支持物料库存预警」但演示时发现根本没做这个功能或者做了但字段名不叫这个名字。原因文档写得太早代码是后来改的两边不同步。这是毕设答辩翻车率最高的一种情况老师随便抽一个功能让现场演示点几下发现没有。解决把文档维护改成「代码每写完一个模块立刻更新对应文档段落」。具体做法是在docs目录里建一个功能核对表.md列所有模块每个模块标注文档是否更新、代码是否完成、演示是否有截图。答辩前一晚对照这张表过一遍所有功能过不掉的直接砍掉也不要留一个写进论文但跑不通的。诚实比完美重要老师更接受「这个功能没做」而不是「论文写了但现场跑不出来」。6. 从能跑到好用加一个操作日志模块让答辩更有说服力6.1 用中间件记录用户操作不改业务代码到了这个阶段系统已经能跑工单、库存、报表都有了。如果想让答辩时老师眼前一亮我建议加一个操作日志模块。这个模块的价值在于它不依赖任何业务代码——用一个 Django 中间件就能记录所有用户的请求写进数据库或者日志文件。中间件的实现思路是每次请求进来时记录时间、用户、请求路径、请求方法每次请求出去时记录响应状态码。这样老师问「怎么知道谁修改了工单」你可以直接展示记录表。# middleware.py 文件 import time from django.utils.deprecation import MiddlewareMixin class OperationLogMiddleware(MiddlewareMixin): def process_request(self, request): request._start_time time.time() def process_response(self, request, response): # 过滤静态文件和后台页面只记录业务操作 if request.path.startswith(/static/) or request.path.startswith(/admin/): return response # 这里写数据库入库逻辑或写日志文件 user request.user.username if request.user.is_authenticated else anonymous log_line f{time.strftime(%Y-%m-%d %H:%M:%S)} | {user} | {request.method} {request.path} | {response.status_code} with open(logs/operation.log, a, encodingutf-8) as f: f.write(log_line \n) return response这个中间件的关键在process_response中过滤了/admin/和/static/否则后台每点一次就被刷一条日志真正的业务操作反而被淹没。日志以追加写方式打开避免每次打开文件覆盖旧内容。把这个中间件注册到 settings.py 的MIDDLEWARE列表末尾MIDDLEWARE [ # ... 默认的中间件不要动 ... mes.middleware.OperationLogMiddleware, # 自定义的放最后 ]注册后无需改任何视图代码所有业务请求都自动留下痕迹。答辩时演示操作几个按钮然后打开日志文件展示记录这个动作的冲击力远超任何概念讲解——它说明你考虑了系统的可追溯性和安全性这是「能用」和「好用」之间的关键差距。6.2 答辩演示版的最后验证从安装到展示的一遍通关系统写完后我强烈建议做一件事在虚拟机里按部署文档从零开始装一遍。这不是浪费时间这是对自己交付的系统做一次完整验证也是我发现文档漏洞最快的办法。具体步骤是装 Python → 建虚拟环境 → 拉代码 → 装依赖 → 迁移数据库 → 导入准备数据 → 创建超级管理员 → 启动服务 → 跑一遍核心流程建物料、建 BOM、下工单、领料、开工、完工、看报表。每次实际操作和文档不一致的地方都改文档而不是改操作去迁就文档。这个习惯是我从吃了亏之后养成的——有次我自己按文档部署漏了pip install mysqlclient这一步卡了半小时从那以后所有交付的文档我都亲自走一遍。最后给这个方向做一个价值判断Python 工厂管理系统作为毕设难度适中、业务边界清晰、文档容易写实值得投入。它的上限取决于你能不能把「流程状态」「库存流水」「操作日志」这三个非表面功能讲透。把这些做扎实再配合这份能把人教会部署的文档你的毕设拿优秀不敢保证但答辩时心里有底是完全够的。希望这篇笔记能帮到你。本文还有配套的精品资源点击获取
