
简介面向高校毕业设计场景的Python学业预警系统完整项目包覆盖管理员与学生双端功能模块包含菜单管理、预警分析、学生信息/成绩管理、用户权限管理以及个人信息与学习计划等模块系统基于Django框架与MySQL数据库构建代码结构清晰适合作为计算机专业毕业设计参考或课程综合实践项目。整个资源包共323个文件约10.53MB含Python后端源码、前端HTML/CSS/JavaScript页面、演示用GIF动图、MySQL数据库SQL脚本及Word开发文档等文件类型覆盖前端展示、后端逻辑、数据库配置与项目说明目录结构明确便于快速部署与二次开发。该资源已有53人学习/下载并附有可运行的完整源码、数据库文件和开发文档同时提供调试问题咨询支持。对于需要完成高校学生学业预警方向毕设的同学可节省大量环境搭建与排错时间快速上手并理解系统实现细节。1. 高校学生学业预警系统比普通成绩管理多出来的核心能力学生学业预警系统本质上不是把成绩从 Excel 挪到网页上而是构建了一条「数据采集 → 规则判定 → 分级预警 → 干预跟踪」的完整链路。管理员可以在后台按班级、学期、成绩区间自由组合查询条件系统自动识别挂科数量超标或平均分下滑的学生并生成预警记录学生登录后能看到个人成绩、预警状态和专属学习计划。与普通的学生信息管理系统相比多出来的核心部分是「预警分析」这一层业务逻辑它是用 Django ORM 的聚合查询配合可配置阈值来实现的而不是在视图里硬编码 if-else。这套系统的技术栈是 Django Python MySQL项目压缩包里包含了完整源码、数据库 SQL 文件、开发文档和论文相关材料导入数据库后基本就能跑起来。它比较适合两类人一是计算机相关专业的学生拿来做毕业设计二是想快速搭一个学院级学业管理后台的开发者。下面我按从零搭建的顺序把环境配置、数据建模、预警算法和部署排错讲透。2. 环境版本选型与 Django 项目骨架搭建2.1 Python、Django、MySQL 的版本匹配这套系统从依赖和文档结构看采用的是 Django 2.x 系列加 Python 3.6 以上加 MySQL 5.7 的组合这也是这类毕业设计最常见的版本搭配。Django 2.2 是长期支持版本ORM 语法稳定、第三方扩展兼容性好网上能查到的资料也最多遇到问题基本都能搜到解决方案。如果本机是 Python 3.10 以上直接安装Django2.2.28也能正常跑只是启动时可能会有一些django.utils.encoding.force_text之类的弃用警告不影响核心功能。MySQL 建议用 5.7 或 8.0但 8.0 默认的caching_sha2_password认证插件与 Django 的旧版 MySQLdb 驱动不兼容需要把用户认证方式改成mysql_native_password否则迁移数据库时会报认证失败。建议用虚拟环境隔离依赖避免污染全局 Python 环境。下面这套命令我每次新建项目都会先跑一遍# 创建虚拟环境 python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境Linux/macOS source venv/bin/activate # 安装核心依赖 pip install django2.2.28 mysqlclient PyMySQL # 验证 Django 版本 python -m django --version这段命令首先创建独立的 Python 运行环境venv会把后续安装的包全部隔离在这个目录里避免和系统其他项目的依赖冲突。mysqlclient是 Django 连接 MySQL 的高性能驱动在 Windows 上如果安装报错可以改用pymysql加一行注册代码的方式。使用pymysql时需要在这个项目的__init__.py中写入补丁代码import pymysql pymysql.install_as_MySQLdb()这个补丁的作用是把pymysql注册成 Django 底层期望的MySQLdb名称Django 的 MySQL 后端在导入连接器时会查找MySQLdb不补这一行会直接报ModuleNotFoundError: No module named MySQLdb。整体来看Django 版本决定 ORM 语法和迁移工具的可用性Python 版本决定第三方库的兼容边界MySQL 版本决定字符集和认证方式三者要作为一个整体对齐。2.2 项目结构和应用拆分项目骨架建议按照「一个项目 多个应用」的方式来组织而不是把管理员、学生、成绩、预警全部塞进一个views.py里。对这套预警系统推荐按用户、学生、成绩、预警四个维度拆分应用各自负责独立的数据模型和业务逻辑。# 创建项目 django-admin startproject warning_system # 进入项目目录 cd warning_system # 创建四个核心应用 python manage.py startapp users python manage.py startapp students python manage.py startapp scores python manage.py startapp early_warning创建完成后需要把应用注册到warning_system/settings.py的INSTALLED_APPS列表里。同时把数据库连接从默认的 SQLite 切换成 MySQL关键配置如下INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, users, students, scores, early_warning, ] DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: warning_system, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }这里ENGINE使用django.db.backends.mysql告诉 Django 通过 MySQLdb/PyMySQL 驱动访问 MySQL。NAME对应 MySQL 中已创建的数据库名称需要在命令行里提前执行CREATE DATABASE warning_system CHARACTER SET utf8mb4;建好否则后面迁移会报数据库不存在。OPTIONS里强制指定utf8mb4是为了完整支持中文、emoji 和生僻字MySQL 默认的utf8mb3在存四字节字符时会直接报错。静态文件配置也需要提前补齐这和压缩包里那一堆bootstrap.min.css、layui.css、admin.css直接相关import os BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATIC_URL /static/ STATICFILES_DIRS [os.path.join(BASE_DIR, static)] MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)STATICFILES_DIRS告诉 Django 在哪些目录寻找静态资源文件下载项目里的bootstrap.min.css、animate.min.css、layui.css、izeetak.css等全部放在根目录的static文件夹下就能被正确识别。MEDIA_ROOT用于存放用户上传的图片和文件预警系统中如果学生上传证明材料就会写入这个目录。很多同学下载源码后登录页面只有纯文本没有样式几乎都是漏掉了STATICFILES_DIRS这段配置。3. 核心数据模型设计与 ORM 迁移实战3.1 学生、课程、成绩、预警四张核心表数据模型是整个预警系统的地基设计时要反过来想管理员最频繁的查询是什么是看某个学生的成绩曲线、按班级统计挂科人数、按预警等级过滤学生列表。因此表结构要围绕「学生—课程—成绩—预警状态」四个核心实体展开并且实体之间通过外键建立关联。学生信息表通过OneToOneField与 Django 内置的User表一对一关联这样登录认证直接复用 Django 的权限系统密码哈希也不用手动实现。核心模型定义如下from django.db import models from django.contrib.auth.models import User class Student(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, verbose_name关联账户) student_no models.CharField(max_length20, uniqueTrue, verbose_name学号) name models.CharField(max_length50, verbose_name姓名) gender models.CharField(max_length10, choices((男, 男), (女, 女)), verbose_name性别) class_name models.CharField(max_length100, verbose_name班级) major models.CharField(max_length100, verbose_name专业) enrollment_date models.DateField(auto_now_addTrue, verbose_name入学时间) phone models.CharField(max_length20, blankTrue, verbose_name联系方式) status models.CharField(max_length20, default在读, verbose_name在校状态) class Meta: db_table t_student verbose_name 学生信息 ordering [student_no]OneToOneField保证了「一个账户只对应一个学生」的约束on_deletemodels.CASCADE表示用户账号被删除时关联的学生记录也一并删除避免产生孤儿数据。student_no设置uniqueTrue后数据库层面就保证了学号不可重复比在视图函数里手动查重更可靠。db_table指定物理表名为t_studentordering让所有列表查询默认按学号升序排列。课程表相对简单只需要记录课程编号、名称、学分和开课学期class Course(models.Model): course_no models.CharField(max_length20, uniqueTrue, verbose_name课程编号) course_name models.CharField(max_length100, verbose_name课程名称) credit models.DecimalField(max_digits3, decimal_places1, verbose_name学分) semester models.CharField(max_length20, verbose_name开课学期) teacher models.CharField(max_length50, blankTrue, verbose_name授课教师) class Meta: db_table t_course verbose_name 课程信息这里credit用DecimalField而不是FloatField是个容易忽视的细节。学分后续要参与平均学分绩点计算浮点数的二进制存储方式会引入精度误差而DecimalField在数据库层面对应DECIMAL精确类型累计求和时不会出现小数点后多出几位的情况。成绩表是整个预警系统里最重要的数据表它的设计直接决定预警分析模块能做什么class Score(models.Model): student models.ForeignKey(Student, on_deletemodels.CASCADE, verbose_name学生) course models.ForeignKey(Course, on_deletemodels.CASCADE, verbose_name课程) score models.DecimalField(max_digits5, decimal_places1, verbose_name成绩) credit_point models.DecimalField(max_digits4, decimal_places2, blankTrue, nullTrue, verbose_name学分绩点) exam_date models.DateField(blankTrue, nullTrue, verbose_name考试时间) remark models.CharField(max_length200, blankTrue, verbose_name备注) class Meta: db_table t_score unique_together (student, course) verbose_name 学生成绩unique_together (student, course)是这行代码最核心的约束它保证同一个学生对同一门课程只能有一条成绩记录从源头杜绝了重复录入导致的统计失真。credit_point是冗余字段在成绩录入时同步计算写入这样预警分析模块做绩点排序时不需要动态计算查询性能更好。预警记录表负责保存每次触发的预警结果它是预警分析的输出端class EarlyWarning(models.Model): student models.ForeignKey(Student, on_deletemodels.CASCADE, verbose_name学生) warning_type models.CharField(max_length50, verbose_name预警类型) warning_level models.CharField( max_length10, choices((hint, 提示), (mild, 轻度警告), (severe, 严重警告)), defaulthint, verbose_name预警等级 ) warning_content models.TextField(verbose_name预警内容) semester models.CharField(max_length20, verbose_name预警学期) create_time models.DateTimeField(auto_now_addTrue, verbose_name预警时间) is_processed models.BooleanField(defaultFalse, verbose_name是否已处理) class Meta: db_table t_early_warning ordering [-create_time]warning_level用choices限定为三个等级前端渲染时可以直接映射成不同颜色的标签。is_processed表示这个预警是否已经被管理员干预处理预警分析首页默认只统计未处理记录。semester字段非常重要因为同一学生在不同学期可能多次触发预警后续要做干预效果分析时按学期筛选就靠这个字段。3.2 使用 Django 迁移系统建表模型定义完成后执行迁移命令把模型转换成 MySQL 里的真实表结构# 生成迁移文件 python manage.py makemigrations # 执行迁移 python manage.py migrate # 查看某次迁移对应的原生 SQL python manage.py sqlmigrate scores 0001makemigrations会扫描模型定义和当前数据库的差异生成增量迁移文件。migrate根据迁移文件依次执行对应的 SQL 建表语句。sqlmigrate用来查看要执行的 SQL 原文排查字段类型和索引是否符合预期。如果迁移时报django.db.utils.OperationalError: (1366, Incorrect string value)说明数据库连接字符集没有设置成utf8mb4回到 MySQL 里执行下面的 SQL 修改库和表的默认字符集即可ALTER DATABASE warning_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;3.3 造测试数据验证模型表建好后用 Django Shell 批量造几条关联数据验证外键关系和查询链路from django.contrib.auth.models import User from students.models import Student user User.objects.create_user(username2022001, passwordtest123456) stu Student.objects.create(useruser, student_no2022001, name张三, gender男, class_name计算机2101班, major计算机科学与技术)create_user和create的区别是前者会调用密码哈希算法把明文密码转换为不可逆的哈希串存储后者不会做这个处理直接存明文登录校验必然失败。创建成绩记录时通过外键关联学生和课程from scores.models import Course, Score course Course.objects.create(course_noC001, course_name高等数学, credit4, semester2023-2024-1) Score.objects.create(studentstu, coursecourse, score58)执行python manage.py shell进入交互式环境后粘贴以上代码如果没有任何报错说明数据模型和数据库连接都已经正常。此时去 MySQL 里查询t_score表会看到一条张三的 58 分成绩记录。4. 预警分析算法与视图层实现4.1 预警规则的可配置设计预警分析模块最容易犯的错误是把判定逻辑硬编码在视图函数里等到管理员想调整阈值时只能改代码重新部署。更合理的做法是把规则单独抽取成策略类或者常量配置预警阈值以字典或表格形式集中管理。预警等级触发条件提示单科成绩低于 60 分轻度警告同一学期挂科 1 门但平均分不低于 60或平均分低于 70严重警告同一学期挂科 3 门及以上或平均分低于 60这张表是后面写预警判定逻辑的规则依据。规则本身不是死的管理员在页面上调整阈值后只要同步更新对应配置项即可不需要修改任何业务代码。需要注意阈值设置不能太激进否则频繁触发预警会让教师产生「狼来了」心理反而降低了预警系统的可信度。4.2 用 Django ORM 实现预警判定预警生成逻辑的核心是统计某个学生在某个学期的挂科数量和平均分。下面这段代码是按照单学生维度做逐条判断的标准实现逻辑清晰、容易调试from django.db.models import Avg, Count, Q from students.models import Student from scores.models import Score from early_warning.models import EarlyWarning def check_student_warning(student_id, semester): student Student.objects.get(pkstudent_id) # 查询该生指定学期的所有课程成绩 scores Score.objects.filter( studentstudent, course__semestersemester ) fail_count scores.filter(score__lt60).count() avg_score scores.aggregate(avgAvg(score))[avg] or 0 # 严重警告挂科3门及以上 或 平均分低于60 if fail_count 3 or (fail_count 1 and avg_score 60): level severe content f{semester} 学期挂科 {fail_count} 门平均分 {avg_score:.1f}请重点关注 # 轻度警告挂科1门及以上 或 平均分低于70 elif fail_count 1 or avg_score 70: level mild content f{semester} 学期挂科 {fail_count} 门平均分 {avg_score:.1f}需要加强学习管理 else: # 没有触发任何阈值直接返回不写入预警表 return # 同一学生同一学期同一类型只保留一条预警记录重复执行不会产生重复数据 EarlyWarning.objects.update_or_create( studentstudent, warning_typegrade, semestersemester, defaults{ warning_level: level, warning_content: content, is_processed: False, } )这段代码里course__semester使用了 Django ORM 的双下划线跨表查询语法含义是「通过成绩关联的课程表访问课程的学期字段」。filter(score__lt60)中的lt是 less than 的缩写生成 SQL 时对应WHERE score 60。aggregate(avgAvg(score))返回一个字典[avg]取平均值如果该生没有任何成绩avg会是None所以加了or 0兜底。update_or_create是这个场景下的最优选择它会先按student、warning_type、semester三个条件查找存在则更新预警等级和内容不存在则新建记录。这避免了反复手工查重也不会因为成绩被修改而产生堆积的过期预警。批量生成全校预警时单学生循环方式性能不够我一般会改用annotate做数据库层聚合一次性统计所有学生的挂科数和平均分from django.db.models import Count, Avg, Q def generate_all_warnings(semester): stats ( Score.objects .filter(course__semestersemester) .values(student_id) .annotate( fail_countCount(id, filterQ(score__lt60)), avg_scoreAvg(score) ) .order_by(-fail_count) ) for item in stats: if item[fail_count] 3 or item[avg_score] 60: EarlyWarning.objects.update_or_create( student_iditem[student_id], warning_typegrade, semestersemester, defaults{ warning_level: severe, warning_content: f挂科 {item[fail_count]} 门平均分 {item[avg_score]:.1f}, is_processed: False, } )这里的Count(id, filterQ(score__lt60))是 Django 2.0 后支持的带条件聚合语法数据库在聚合阶段就完成条件过滤只计算挂科数量不需要把所有成绩先查出来再在 Python 里数。values(student_id)先按照学生分组annotate给每组增加fail_count和avg_score两个统计字段。和逐学生循环相比数据量一万条时响应时间能从秒级降到百毫秒级。4.3 成绩变更自动触发预警重算预警不能只靠管理员手动点按钮成绩导入或修改后应该自动触发重算。Django 信号机制是标准做法在early_warning/signals.py中绑定成绩模型的post_save信号from django.db.models.signals import post_save from django.dispatch import receiver from scores.models import Score from early_warning.services import check_student_warning receiver(post_save, senderScore) def score_saved_trigger_warning(sender, instance, **kwargs): semester instance.course.semester check_student_warning(instance.student_id, semester)post_save在每次Score执行save()之后触发instance就是刚保存的成绩对象。这里instance.course.semester涉及一次跨表查询如果成绩批量导入一万条就会产生一万次额外查询性能上是灾难。用这份源码做二次开发时建议把semester冗余到Score表中或者在批量导入时关闭信号导入完成后只做一次全量重算。4.4 预警分析页面的数据组装管理员端的预警分析页面不需要每次都做复杂统计直接读取t_early_warning表按条件过滤即可。页面要展示的核心数据包括预警等级分布、各班级预警人数、未处理预警明细。视图层用类视图加get_queryset改写from django.views.generic import ListView from django.db.models import Count from early_warning.models import EarlyWarning class WarningAnalysisView(ListView): template_name admin/warning_analysis.html model EarlyWarning paginate_by 20 def get_queryset(self): return EarlyWarning.objects.select_related( student ).values( student__name, student__student_no, student__class_name, warning_level, warning_content, create_time, is_processed ).order_by(-create_time) def get_context_data(self, **kwargs): context super().get_context_data(**kwargs) context[level_stats] EarlyWarning.objects.values( warning_level ).annotate( countCount(id) ) return contextselect_related(student)通过 SQL JOIN 预先把学生表中的姓名、学号、班级字段查出来模板里访问item.student__name时不再发起额外查询。values方法把大字段warning_content之外的字段也限制住了传输数据量更小。is_processed字段在模板里用来区分已处理和未处理视图未处理的预警行显示醒目标记。5. 角色权限控制与学生模块实现5.1 不同角色的数据隔离方案预警系统涉及管理员、教师、学生三类角色权限控制不能只在页面入口做判断必须落实到视图层的数据过滤上。最简单的方案是利用 Django 内置的User.is_staff和is_superuser字段再加一个判断逻辑学生角色通过OneToOneField关联学生表管理员和教师走后台。角色登录入口可访问数据范围超级管理员Django Admin 或自定义后台全部功能模块和全部数据教师自定义后台学生信息、成绩管理、预警分析学生自定义前台仅本人的信息、成绩、预警和学习计划5.2 学生个人中心的数据隔离学生个人中心最关键的需求是登录后只能看到属于自己的成绩和预警绝对不能通过修改 URL 中的 ID 查看其他同学的数据。使用DetailView时不要去读 URL 参数中的pk而是强制从当前登录态取学生记录from django.views.generic import DetailView from django.contrib.auth.mixins import LoginRequiredMixin from students.models import Student class StudentProfileView(LoginRequiredMixin, DetailView): model Student template_name user/profile.html context_object_name student def get_object(self, querysetNone): return Student.objects.get(userself.request.user)LoginRequiredMixin在未登录时自动跳转到登录页get_object强制通过request.user反向关联学生表。这样即使攻击者在 URL 里改成其他人的 ID视图也只返回当前登录账号对应的学生数据。5.3 学生端成绩查询与学习计划成绩查询视图同样绑定到当前登录用户from django.shortcuts import render from students.models import Student from scores.models import Score def student_scores_view(request): student Student.objects.get(userrequest.user) scores Score.objects.filter(studentstudent).select_related(course) return render(request, user/scores.html, {scores: scores})select_related(course)一次 JOIN 查出课程名称和学分模板中遍历scores时访问score.course.course_name不会产生额外 SQL。成绩列表加上{% if score.score 60 %}的模板判断挂科行用红色高亮显示加载bootstrap.min.css后直接用table-danger类即可。学习计划模块不只是一个简单的增删改查它应该能关联到当前预警状态。推荐在计划表中增加一个target_course外键学生在制定计划时可以选择挂科的课程将学习计划与具体课程绑定这样计划更有针对性。from django.db import models class StudyPlan(models.Model): student models.ForeignKey(Student, on_deletemodels.CASCADE, verbose_name学生) title models.CharField(max_length100, verbose_name计划标题) content models.TextField(verbose_name计划内容) target_course models.ForeignKey(Course, on_deletemodels.SET_NULL, blankTrue, nullTrue, verbose_name目标课程) create_time models.DateTimeField(auto_now_addTrue, verbose_name创建时间) deadline models.DateField(blankTrue, nullTrue, verbose_name计划截止日期) is_finished models.BooleanField(defaultFalse, verbose_name是否完成) class Meta: db_table t_study_plan ordering [-create_time]新增学习计划的视图函数from django.shortcuts import redirect from django.contrib import messages from students.models import Student def add_study_plan(request): if request.method POST: student Student.objects.get(userrequest.user) plan StudyPlan.objects.create( studentstudent, titlerequest.POST.get(title), contentrequest.POST.get(content), target_course_idrequest.POST.get(target_course) or None, deadlinerequest.POST.get(deadline) or None, ) messages.success(request, 学习计划创建成功) return redirect(user_study_plans) return redirect(user_study_plans)request.POST.get(target_course) or None这句用来处理前端下拉框未选课程的情况。如果直接提交空字符串给外键字段Django 会报完整性错误转成None后数据库层就允许空值了。messages.success配合模板里的{% for message in messages %}循环渲染出操作提示比手动在页面写 alert 更规范。5.4 菜单模块的数据库化设计后台管理端的左侧导航菜单不建议写死用数据库存菜单项每次登录后根据角色动态渲染扩展新功能时不用改模板。菜单模型用自关联实现多级菜单class Menu(models.Model): name models.CharField(max_length50, verbose_name菜单名称) url_name models.CharField(max_length100, verbose_name路由名称) icon models.CharField(max_length100, blankTrue, verbose_name图标) parent models.ForeignKey(self, on_deletemodels.CASCADE, blankTrue, nullTrue, verbose_name父级菜单) sort models.IntegerField(default0, verbose_name排序) class Meta: db_table t_menu ordering [sort]parent自关联到自身nullTrue表示顶级菜单。模板中用{% url menu.url_name %}动态生成链接前端layui.css和admin.css已经提供了现成的侧边栏样式只需要把菜单树渲染成对应的 HTML 结构即可。6. 系统部署、性能优化与高频排错6.1 开发环境到生产环境的配置切换从runserver开发模式切换到生产部署第一步是修改settings.py里的调试开关和允许访问域名DEBUG False ALLOWED_HOSTS [your-server-ip, your-domain.com] # 静态文件收集目录 STATIC_ROOT os.path.join(BASE_DIR, staticfiles)DEBUG False后 Django 不再处理静态文件需要执行python manage.py collectstatic把所有应用中的静态文件复制到STATIC_ROOT。这一步经常会遇到文件找不到的问题排查思路很简单确认bootstrap.min.css、layui.css这些文件真实存在于某个应用的static目录或STATICFILES_DIRS指定的目录里然后重新执行收集命令。6.2 数据库导入与初始化项目数据库通常已导出为.sql文件导入前要先建库再导入# 登录 MySQL 创建数据库 mysql -u root -p -e CREATE DATABASE warning_system CHARACTER SET utf8mb4; # 导入数据 mysql -u root -p warning_system warning_system.sql导入操作成功后进入项目目录用createsuperuser创建新的管理员账号或使用源码中已有的账号需先确认密码强度。导入后建议立即修改密码避免使用开发文档里默认的弱口令。6.3 Gunicorn 和 Nginx 反向代理部署项目需要通过 Gunicorn 启动执行命令时注意工作目录必须是项目根目录pip install gunicorn gunicorn warning_system.wsgi:application \ --bind 127.0.0.1:8001 \ --workers 4 \ --timeout 60 \ --access-logfile logs/access.log \ --error-logfile logs/error.logwarning_system.wsgi:application表示从wsgi.py中加载application对象--workers 4是多进程模式一般设为 CPU 核数的两倍加一。--timeout 60防止某些慢查询把 worker 卡死。如果项目中没有logs目录启动前需要先mkdir -p logs。Nginx 配置反向代理指向 Gunicorn 监听的端口静态文件直接从STATIC_ROOT目录服务server { listen 80; server_name your-server-ip; location /static/ { alias /opt/warning_system/staticfiles/; } location /media/ { alias /opt/warning_system/media/; } location / { proxy_pass http://127.0.0.1:8001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }配置好 Nginx 后先执行nginx -t检查语法再执行systemctl reload nginx加载配置。这里很容易踩的坑是alias路径末尾斜杠问题alias /opt/warning_system/staticfiles/;末尾斜杠必须存在否则请求/static/admin.css时会映射到错误的文件路径。6.4 高频报错定位与性能排查清单部署和使用过程中最容易碰到的问题集中在以下几类我把原因和处理方式归纳成一张速查表报错现象根本原因处理方式ModuleNotFoundError: No module named MySQLdb缺少 MySQL 驱动或 PyMySQL 未注册安装mysqlclient或补pymysql.install_as_MySQLdb()OperationalError: (2003, Cant connect to MySQL server)MySQL 服务未启动或端口配置错误systemctl status mysql检查服务状态页面无样式静态文件路径配置错误或未收集检查STATICFILES_DIRS和collectstatic执行结果模板报TemplateDoesNotExistDIRS未指向项目根 templates 目录在TEMPLATES配置中增加os.path.join(BASE_DIR, templates)时间差 8 小时时区设置为 UTC修改TIME_ZONE Asia/Shanghai管理员密码忘记数据库用户表密码哈希不可逆使用createsuperuser新建管理员再登录如果页面响应缓慢优先用django-debug-toolbar定位 SQL 查询次数和耗时常见的性能瓶颈是循环读取外键导致的 N1 查询代码层面加上select_related和prefetch_related能解决大部分问题。数据库层面再针对高频查询字段补索引预警系统场景下学生表的class_name、成绩表的student_id和course_id三个索引收益最大ALTER TABLE t_student ADD INDEX idx_class_name (class_name); ALTER TABLE t_score ADD INDEX idx_student (student_id); ALTER TABLE t_score ADD INDEX idx_course (course_id);这三个索引覆盖了预警分析中最常出现的查询条件按班级统计数据、按学生查成绩、按课程查缺考加上后联表查询的响应时间会有明显下降。索引不是越多越好针对实际查询语句去建才是正确思路。部署完成后把源码里预置的测试数据清空再导入真实学生和成绩数据然后按照「管理员录入成绩 → 自动生成预警 → 学生端查看预警 → 制定学习计划」这条完整链路跑一遍确认每个环节数据都正确流转系统才算真正交付可用。本文还有配套的精品资源点击获取