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

资讯详情

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

Python高校学业预警系统:基于Django与MySQL的规则引擎实战

Python高校学业预警系统:基于Django与MySQL的规则引擎实战 简介基于 Python 的高校学生学业预警系统资源包面向计算机相关专业毕业设计与课程设计人群帮助快速搭建包含学生成绩分析、预警规则判断、前后端管理模块的可运行项目整体功能完整、界面清晰。包内共 319 个文件涵盖 27 个 Python 源文件、数据库 SQL 脚本、前端 HTML/CSS/JS 页面以及 75 个 GIF 操作演示整体压缩后仅 4.19MB结构清晰支持 PyCharm 与 MySQL 环境直接部署使用。目前已有 116 人学习浏览项目经过严格调试可稳定运行。通过这份资源可以获取完整项目源码、数据库表结构与初始化数据、依赖配置说明同时还能看到 layui、Bootstrap、ECharts 等前端组件在管理界面中的实际用法。这些内容对理解学业预警系统的数据流转、修改核心业务逻辑以及准备毕业设计答辩都有直接帮助。1. 高校学业预警系统一个把成绩数据变成预警名单的 Python 毕设包一到期末教务把成绩单导成 Excel辅导员对着表格数挂科门数这个画面我在不止一个学校见过。真到出名单那一步补考报名往往已经截止预警就变成了马后炮。这套基于 Python 的高校学业预警系统核心就是把这个滞后补上学生、班级、课程、成绩全部落入 MySQL由规则引擎按学期扫描自动把学生划进黄色、橙色、红色三个预警等级生成可查、可导出、可追踪处理状态的预警名单。它不只是一份能交差的 Python 毕业设计源码而是一条完整的预警链路——从数据导入、规则判定到页面展示都有现成代码和数据库脚本。拿来做毕设你不用从零搭表结构拿来做课设有数据库文件和部署教程能省下大量造数据的时间。下面我按实际拆包的顺序把目录骨架、建库过程、规则引擎和踩过的坑逐个过一遍。2. 先跑通骨架目录结构、依赖安装与数据库初始化2.1 拿到压缩包先别急着启动目录骨架与入口确认解压之后第一件事不是双击运行而是先把压缩包里的目录看一遍。这类 Python Web 毕设项目一般走 Django 或 Flask 的架子不管哪个框架包里通常都有这些部分前端模板目录templates、静态资源目录static、项目配置模块Django 是settings.pyFlask 是config.py、数据库导出的.sql文件、依赖清单requirements.txt以及一份 PDF 或 Markdown 格式的部署教程。我第一次拆类似包时图快直接跑运行命令结果报错找不到模块config.env后来发现项目的数据库密码和密钥都写在.env文件里而.env一般不进压缩包。所以拿到资源后的标准动作是先打开requirements.txt看 Web 框架版本和数据库驱动再看部署教程里有没有要求手动创建环境变量最后才轮到装依赖。cd student-warning-system python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install -r requirements.txt虚拟环境的作用是隔离依赖避免和你机器上其他项目的包版本冲突。requirements.txt里通常固定了 Django/Flask 版本、MySQL 驱动、以及openpyxl这类导入导出 Excel 用的库。装完依赖后别急着启动先去配置文件里找数据库连接块这一步决定了你连的是 MySQL 还是 SQLite。如果pip install阶段报编译错误多半是mysqlclient的问题——它在 Windows 上需要 Visual C 构建工具。一个常见替代方案是把驱动换成PyMySQL在配置入口处加一句import pymysql; pymysql.install_as_MySQLdb()能省去一堆编译麻烦。2.2 建库导数据SQL 脚本、连接配置与启动命令大多数毕设项目的默认数据库账号是root、密码为空主机127.0.0.1、端口3306。你本机如果没有 MySQL或密码不是空就得先把服务启动再按脚本建库导数据最后回来改配置。数据库脚本里会带建库语句执行前看清楚脚本里有没有CREATE DATABASE如果没有就先手动建库。CREATE DATABASE student_warning DEFAULT CHARACTER SET utf8mb4; USE student_warning; SOURCE /path/to/student_warning.sql;Navicat 用户可以直接右键「运行 SQL 文件」效果一样。重点说一下字符集建库语句里的utf8mb4不要省它和utf8的区别在于能存四字节字符中文和特殊符号都能正常写入。导入完成之后打开配置文件把连接信息改成你自己的账号密码DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: student_warning, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } }上面是 Django 的写法Flask 项目则是在config.py里维护一个SQLALCHEMY_DATABASE_URI字符串内容大同小异。改完配置后按下面顺序验证链路python manage.py makemigrations --check python manage.py migrate python manage.py createsuperuser python manage.py runservermakemigrations --check只做检查不写入能快速暴露模型和数据库是否对得上migrate把 Django 内置表补齐createsuperuser创建后台管理员账号runserver起来后访问/admin能看到数据表里已经导入了初始数据。如果启动时卡在密码认证失败不要急着卸载 MySQL八成是认证插件的问题具体解法放到第 5 章。2.3 后台入口长什么样登录 admin 核对功能入口项目能启动后先用超管账号登录 Django admin把菜单从头到尾点一遍确认有哪些可用的功能模块。这一步的核心目的不是看效果而是建立「这个包到底做到什么程度」的判断是只有成绩录入和展示还是已经包含预警记录、学生管理、班级管理、辅导员分配这些完整业务。一份合格的高校学业预警系统后台通常至少有四个模块学生信息管理维护学号、姓名、班级、入学年份、课程管理课程编号、学分、开课学期、成绩管理学生课程成绩、考试日期、预警记录管理预警等级、原因、处理状态。你在 admin 里看到这些菜单说明数据库脚本导入完整如果菜单明显比预期少优先怀疑 SQL 脚本没导全而不是功能没写。我拆包的习惯是在这一步顺手打开 URL 路由文件把urlpatterns里的路径和 admin 菜单做一次对应。因为管理员后台看的是数据维护真正给辅导员用的预警列表、预警详情、导出按钮走的是前台路由。能在这个阶段把功能入口理清后面跑规则引擎时你就知道每个按钮背后对应哪段视图逻辑排错会快很多。3. 数据建模与 ORM 映射学生、课程、成绩与预警记录的表结构3.1 核心表设计为什么预警记录单独建表学业预警系统的事务是典型的「学生—成绩—预警」三段式成绩是事实预警是基于成绩计算出来的结论。表设计上通常围绕以下四张核心表展开。表关键字段用途studentstudent_no, name, class_id, grade_year, phone学生基础信息coursecourse_no, course_name, credit, semester课程与学分信息scorestudent_id, course_id, score_value, exam_date学生各科成绩warning_recordstudent_id, level, reason, check_date, is_processed预警结果记录这里有个设计选择容易被忽略为什么要单独建warning_record而不是在学生表上加一个warning_level字段因为预警是历史事实——这个学期预警了、下学期成绩回升后解除预警整个过程需要留痕。单独建表后每次扫描生成新预警记录可以追溯每个学生历次预警时间、等级和原因。如果只更新学生表字段历史记录全丢辅导员想查「这个学生上学期有没有被预警过」就查不到。level字段的取值一般约定为 1、2、3分别对应黄色、橙色、红色预警is_processed记录这条预警是否已被辅导员处理标记已处理的数据不会被下一次扫描覆盖。3.2 Django Model 映射外键、索引与 on_delete 的选择数据库脚本和 ORM 模型是同一套结构的两种表达。把表结构翻译成 Django Model 时外键关系、级联删除策略和索引设计三个点最容易写偏。下面只贴和预警统计直接相关的两个模型from django.db import models class Student(models.Model): student_no models.CharField(max_length20, uniqueTrue) name models.CharField(max_length50) class_info models.ForeignKey(ClassInfo, on_deletemodels.PROTECT, db_indexTrue) grade_year models.CharField(max_length10) phone models.CharField(max_length11, blankTrue) def __str__(self): return f{self.student_no} {self.name} class Score(models.Model): student models.ForeignKey(Student, on_deletemodels.CASCADE, db_indexTrue) course models.ForeignKey(Course, on_deletemodels.PROTECT) score_value models.DecimalField(max_digits5, decimal_places1) exam_date models.DateField(nullTrue, blankTrue) class Meta: indexes [ models.Index(fields[student, score_value]), ]on_delete的选择是有讲究的ClassInfo被删时用PROTECT防止有人误删班级时连带把整个班的学生全清掉Score上学生被删则用CASCADE删学生就该删成绩留着反而产生一堆孤儿数据。db_indexTrue和Meta.indexes是给后面的预警扫描做铺垫——预警引擎要按学生逐个统计不及格科目数student和score_value上没有索引数据量一上去就是全表扫描。3.3 导入后先做数据自检三条 SQL 验证数据可信度导入 SQL 脚本之后不要急着写业务代码先用三条 SQL 验证数据质量。这一步花五分钟能避免后面调了两小时才发现成绩数据是空的。SELECT COUNT(*) AS stu_cnt FROM student; SELECT COUNT(*) AS score_cnt FROM score; SELECT student.student_no, student.name, COUNT(*) AS fail_cnt FROM score JOIN student ON score.student_id student.id WHERE score.score_value 60 GROUP BY student.student_no;前两句确认主表不是空表score_cnt为 0 说明成绩数据没导进来预警引擎跑出来必然是一片空白。第三句是预警规则的 SQL 原型先手工把每个学生的不及格科目数算出来拿到这份「预期结果」后面第 4 章的扫描命令跑完拿两张名单一比对规则逻辑正不正确一目了然。如果第三句查出来只有零星几行说明初始数据量偏少演示效果不好如果完全没数据就得自己造样例。造数不是随机插要有意识地覆盖边界场景一个全及格的优等生、一个挂一科的、一个挂三科的、一个挂五科的这样三色预警每个等级都能露脸。4. 预警规则引擎三色分级判定与批量扫描实现4.1 规则阈值怎么定挂科门数、绩点与等级的对应关系预警规则是整个系统的灵魂也是毕设答辩时老师最可能追问的部分。「挂科就预警」太粗糙实际学工系统里用的是分级预警挂科越少越轻、越多越重并且通常还会叠加学分绩点维度。一份比较通用的默认规则是黄色预警学期不及格科目 12 门橙色预警学期不及格科目 3 门或平均学分绩点低于 2.0红色预警学期不及格科目 4 门及以上或连续两学期触发橙色预警这套设定的逻辑在于预警不是为了处分学生而是给辅导员排优先级——红色先约谈黄色批量通知。规则里的阈值1 / 2 / 3 / 4应当写成代码里的常量而不是散落在各处 SQL 里。后面学校说「我们标准是挂两门就要谈」你只需要改一处配置详见第 6 章的参数化方案。4.2 扫描命令实现批量判定、去重更新与性能处理预警判定适合写成 Django management command而不是塞进视图函数。视图是用户点开页面才执行管理命令可以随时手工触发也能挂系统定时任务定期跑。文件名run_warning.py放在management/commands/目录下Django 会自动识别。import datetime from django.core.management.base import BaseCommand from warning.models import Student, Score, WarningRecord YELLOW_LIMIT 2 # 1~2 门 黄色 ORANGE_LIMIT 3 # 3 门 橙色 RED_LIMIT 4 # 4 门及以上 红色 class Command(BaseCommand): help 扫描成绩数据生成学期学业预警记录 def handle(self, *args, **options): students Student.objects.all() for stu in students.iterator(chunk_size200): fail_count Score.objects.filter( studentstu, score_value__lt60 ).count() if fail_count 0: continue if fail_count YELLOW_LIMIT: level yellow elif fail_count ORANGE_LIMIT: level orange else: level red WarningRecord.objects.update_or_create( studentstu, defaults{ level: level, reason: f本学期不及格科目 {fail_count} 门, check_date: datetime.date.today(), } ) self.stdout.write(预警扫描完成)三个关键点说清楚。第一iterator(chunk_size200)是分块读学生数量到三千以上时不会一次性把全部对象加载进内存这是大数据量下的基本素养。第二score_value__lt60是 ORM 的过滤语法翻译成 SQL 就是WHERE score_value 60注意不及格的判定用的是百分制小于 60如果你的数据里存在五分制或等级制成绩这里要单独清洗。第三update_or_create以学生为唯一键做更新——同一学生多次扫描只保留最新一条预警记录不会出现重复数据。这里只实现了「挂科门数」单维度绩点维度可以在elif后追加判断把 GPA 计算抽成一个独立方法保持每个分支只做一件事。4.3 预警结果上页面列表查询、N1 优化与 CSV 导出预警记录生成后前台页面要做的核心事是「按等级查列表」和「导出明细」。列表视图的写法直接决定页面响应速度最常见的问题是 N1 查询预警记录关联学生学生又关联班级模板每渲染一行就要查一次库一百条记录打出三百条 SQL。def warning_list(request): level request.GET.get(level, ) records WarningRecord.objects.select_related(student__class_info).all() if level: records records.filter(levellevel) return render(request, warning/list.html, {records: records})select_related(student__class_info)在 Django 里会把预警表、学生表、班级表三张表用 JOIN 一次性查出模板循环时不再发额外查询。filter(levellevel)对应列表页顶部的黄、橙、红三个筛选标签URL 传参形式是?levelorange。导出功能是辅导员的刚需——他们要拿名单去和学生谈话线上看完不够还得落 Excel。常见做法是在列表页加一个导出按钮对应视图直接生成 CSVimport csv from django.http import HttpResponse def export_warning_csv(request): records WarningRecord.objects.select_related(student__class_info).all() resp HttpResponse(content_typetext/csv) resp[Content-Disposition] attachment; filenamewarning_records.csv resp.write(b\xef\xbb\xbf) # UTF-8 BOMExcel 打开不乱码的关键 writer csv.writer(resp) writer.writerow([学号, 姓名, 班级, 预警等级, 预警原因, 记录日期]) for r in records: writer.writerow([ r.student.student_no, r.student.name, r.student.class_info.name, r.level, r.reason, r.check_date ]) return resp这个函数不依赖第三方库浏览器直接触发下载。\xef\xbb\xbf是 UTF-8 的 BOM 头不加它Excel 打开 CSV 时中文列名会乱成一团这个坑我在实际交付时踩过后面每次都补上。5. 避坑排查数据库认证、中文乱码与扫描慢的五个实战案例5.1 MySQL 8 连不上caching_sha2_password 认证插件问题现象runserver启动时抛出OperationalError: Authentication plugin caching_sha2_password cannot be loaded或者直接报 1045 拒绝访问。原因MySQL 8 默认的认证插件是caching_sha2_password而你用的 MySQL 驱动版本偏旧只认老的mysql_native_password协议。还有一种情况是 MySQL 账号密码策略复杂密码里带了、#这类特殊字符配置文件里没转义。解决二选一。本机开发图省事可以把账号认证方式改回旧协议ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;注意这会降低该账号的认证强度仅限本地环境使用。更推荐的路子是升级 PyMySQL 到新版本新版本已经支持caching_sha2_password不动数据库账号更安全。5.2 中文变问号字符集、模板编码与 CSV BOM 问题现象数据库管理工具里能看到中文页面上却全是???或者 CSV 导出后 Excel 打开乱码。原因三个层面。数据库表字符集不是utf8mb4建库时用了默认的latin1或老utf8页面模板没声明 UTF-8CSV 导出漏了 BOM 头。解决建库语句用DEFAULT CHARACTER SET utf8mb4第 2 章已经强调过模板head里加meta charsetutf-8Django 的响应默认 UTF-8一般没问题。CSV 的 BOM 头是难点别嫌麻烦导出时写上resp.write(b\xef\xbb\xbf)这一行Excel 直接打开就不乱。我第一次给辅导员导名单时漏了这行人家反馈「你这表根本没法看」从那以后这行成了我的肌肉记忆。5.3 预警日期差一天时区与 localdate 的坑现象check_date存进去后数据库里显示的日期是昨天或者 admin 后台显示00:00:00。原因Django 项目配置里TIME_ZONE和本机时区不一致数据库连接用的也是 UTC 时间导致日期被按 UTC 计算。这个 bug 很隐蔽白天看不出问题晚上十一点跑扫描存进去就变成第二天。解决settings.py 里显式配置TIME_ZONE Asia/Shanghai USE_TZ True代码里取「今天」别用 Python 原生的date.today()改用django.utils.timezone.localdate()它会按配置时区返回当前日期。预警记录这类日期敏感的数据统一走localdate()最稳。5.4 扫描命令越来越慢成绩表索引缺失现象学生数量到三千、成绩表数据过五万后预警扫描从几秒涨到几分钟。原因这不是 Django ORM 的问题是表索引问题。Score表的student_id和score_value字段都没有索引每次统计不及格门数都在做全表扫描。解决在第 3 章的模型里已经预埋了复合索引fields[student, score_value]执行迁移生效即可。手动建表的话对应 SQL 是ALTER TABLE score ADD INDEX idx_student_score (student_id, score_value);WHERE条件同时按student_id等值过滤和score_value范围过滤复合索引能把扫描范围缩到几百行。我遇到过有人建了索引但不生效最后发现是建了表没跑migrate——模型改了数据库没跟上属于低配版翻车。5.5 重复预警记录唯一约束与 update_or_create 的关系现象同一个学生预警表里有三行一模一样的记录辅导员以为这个学生被预警了三次。原因扫描命令跑了几次每次都执行 insert没有按学生维度去重。老版本的代码直接用create()而不是update_or_create()或者命令本身是对的但之前跑的时候产生了重复数据没有清掉。解决除了命令里用update_or_create更保险的做法是在数据库层加唯一约束双保险class Meta: constraints [ models.UniqueConstraint(fields[student, semester], nameuniq_student_semester) ]这种方式要求semester字段存在用来区分不同学期。如果原表结构没有学期字段退而求其次在命令里先filter(studentstu).delete()再create()效果等同但会多一次删除操作。注意UniqueConstraint在 MySQL 5.7 以下不支持老库只能靠 ORM 层做去重。6. 阈值参数化与 58 分验证把预警规则调到能直接交付规则引擎能用只是第一步能交付给学校用是另一件事。辅导员的真实诉求往往是「这个学期标准变了挂两门就要谈话」你总不能每次都改 Python 文件让服务器重启。更好的做法是把阈值抽到配置文件里让规则和逻辑分离。在settings.py里加一段WARNING_RULES { yellow: {fail_min: 1, fail_max: 2}, orange: {fail_min: 3, fail_max: 3}, red: {fail_min: 4, fail_max: 99}, }然后在扫描命令里不再写死YELLOW_LIMIT而是读配置。这样学校调整标准时运维改配置文件就够了不动业务代码。这是我拆过多个毕设项目后最想强调的一点规则阈值不参数化后面每一次需求变更都是在给自己挖坑。交付前的验证方法我习惯用一个「58 分造数法」来走通整条链路。给某个确定学生插入一条成绩 58 的记录跑扫描命令再查预警记录一共三步python manage.py shell -c from warning.models import Student, Score, Course s Student.objects.first() c Course.objects.first() Score.objects.create(students, coursec, score_value58) python manage.py run_warning python manage.py shell -c from warning.models import Student, WarningRecord stu Student.objects.first() print(list(WarningRecord.objects.filter(studentstu).values_list(level, reason))) 如果第三步查到yellow且原因里写着「不及格科目 1 门」说明从数据写入到规则判定再到结果落库整条链路是通的。然后把这条 58 分记录删掉再跑一次扫描确认预警记录被更新或撤销这就证明了规则引擎不是黑匣子——它的每一步都是可预期、可回归的。造数时注意Score.exam_date字段如果模型里设了nullTrue不传没问题如果没设默认值就得显式传一个日期否则创建记录会报 NOT NULL 约束错误。我刚拆这份学业预警系统时最吃亏的一件事就是拿到压缩包直接启动跑挂了才回头看部署教程结果折腾一个晚上不如先花十分钟查表结构、改数据库连接、跑一条 58 分验证。从那以后我每次拿到 Python 毕业设计包都强制走一遍先看配置文件的数据库连接再导 SQL 核对菜单最后用一条边界数据验证核心模块。这套流程走完资源能不能用、坑在哪基本心中有数。希望帮到你。本文还有配套的精品资源点击获取
返回列表