首页/新闻资讯/正文详情

Flask + MySQL 高校成绩管理系统实战:从环境搭建到存储过程排名

发布时间:2026/9/23 2:07:37 来源:云帆数科 栏目:资讯中心
Flask + MySQL 高校成绩管理系统实战:从环境搭建到存储过程排名
简介这是一套面向高校计算机相关专业学生的成绩管理系统完整项目源码基于Python语言、Flask框架与MySQL数据库开发适合用作期末课程设计、毕业设计参考或Web开发入门练手。项目在PyCharm中可直接打开运行涵盖学生信息维护、课程设置、成绩录入与查询统计等典型教务场景能帮助读者理解前后端交互与数据库设计的完整链路。压缩包共1480个文件以720个py源码和601个pyc编译文件为主体另含dll动态库、exe可执行文件、wheel安装包及html、css、js等前端资源整体约16.35MB目录结构清晰便于按模块定位学习。目前已有104人学习下载。读者可从中获取可运行的Flask项目骨架、MySQL建表与操作逻辑、前端页面样式以及依赖环境配置参考适合对照源码梳理MVC分层思路并在此基础上二次开发。1. 从一张成绩单说起Flask MySQL 的高校成绩管理系统到底解决什么问题期末周刚过教务老师发来一张 Excel里面是三个专业、两百多号人的成绩要求按班级排名、算加权平均分、导出补考名单。手动做一遍要一下午改一个分数还得从头核对。这类重复劳动正是「Python 高校成绩管理系统」要吃掉的东西用 Flask 做后端接口和页面路由用 MySQL 存学生、课程、成绩三张核心表在 PyCharm 里把项目跑起来最后打包成一个能在局域网访问的 Web 应用。它适合两类人——正在找课程设计或毕设题目的学生以及想用一个完整项目把 Python 语法、Flask 路由、MySQL 增删改查串起来的入门者。整套东西不复杂但坑集中在环境配置和数据库连接上下面按「先跑通、再讲透、最后避坑」的顺序拆开。2. 环境先落地PyCharm 建项目、MySQL 建库、Flask 装依赖2.1 为什么选 Flask 而不是 Django高校成绩管理系统的业务边界很清楚学生登录看成绩、教师录入和修改成绩、管理员维护课程和学生名单。没有复杂的权限树也不需要自带后台。Django 自带 ORM、Admin、认证体系开箱即用但目录结构重改一个字段要动 migrationsFlask 是微框架路由、模板、数据库连接都要自己接反而让初学者看清「一个请求从浏览器到数据库再回来」的完整链路。常见做法是 Flask Flask-SQLAlchemy PyMySQL 三件套SQLAlchemy 负责把 Python 对象映射成表PyMySQL 负责和 MySQL 通信。选 Flask 的另一个理由是调试轻改完代码保存即生效不用等重载。2.2 PyCharm 里建项目与虚拟环境打开 PyCharm新建项目时选 Pure PythonLocation 填score_system解释器选 New environment using Virtualenv基础解释器指向本机 Python 3.10 或 3.11。虚拟环境的意义是把 Flask、SQLAlchemy 这些包装在项目目录下的venv里不污染全局。建好后在底部 Terminal 里确认# 确认当前解释器是项目内的 venv which python # Windows 用 where python python --version # 应输出 3.10.x 或 3.11.x如果which python指向系统路径而不是score_system/venv/bin/python说明 PyCharm 没把终端切到虚拟环境去 Settings → Project → Python Interpreter 手动选中 venv 里的解释器。这一步不做后面pip install会装到全局换台机器就复现不了。2.3 安装依赖并锁定版本在项目根目录建requirements.txt内容如下Flask3.0.3 Flask-SQLAlchemy3.1.1 PyMySQL1.1.1 cryptography42.0.8然后执行pip install -r requirements.txtcryptography不是可选项。MySQL 8.0 默认用caching_sha2_password认证插件PyMySQL 连接时必须依赖它做握手缺了会报RuntimeError: cryptography is required for sha256_password。版本号写死是为了避免半年后重装时 Flask 4.x 改了 API 导致代码跑不起来。2.4 MySQL 建库与建表MySQL 8.0 安装时记住 root 密码装完用命令行或 Workbench 连上执行CREATE DATABASE score_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE score_db; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(50) NOT NULL, class_name VARCHAR(50) ); CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, cname VARCHAR(50) NOT NULL, credit DECIMAL(3,1) DEFAULT 0 ); CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, grade DECIMAL(5,2) DEFAULT 0, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (course_id) REFERENCES course(id) );字符集必须用utf8mb4否则学生姓名里的生僻字会变成问号。grade用DECIMAL(5,2)而不是FLOAT因为浮点数存 89.1 可能变成 89.09999算平均分时误差累积。外键约束保证不会出现「成绩表里有学生 id 但学生表里没有这个人」的脏数据。3. 把 Flask 和 MySQL 接起来连接串、模型与第一个查询接口3.1 连接串怎么写才不出错在项目根目录建app.py先写最小可运行版本from flask import Flask, jsonify from flask_sqlalchemy import SQLAlchemy app Flask(__name__) # 格式mysqlpymysql://用户名:密码主机:端口/库名?charsetutf8mb4 app.config[SQLALCHEMY_DATABASE_URI] ( mysqlpymysql://root:你的密码127.0.0.1:3306/score_db?charsetutf8mb4 ) app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False db SQLAlchemy(app) app.route(/ping) def ping(): db.session.execute(db.text(SELECT 1)) return jsonify({status: db ok}) if __name__ __main__: app.run(debugTrue, host0.0.0.0, port5000)连接串里三个地方最容易翻车主机写localhost在某些系统上会走 socket 而不是 TCP报error 2002 (HY000)统一写127.0.0.1更稳密码里如果有或#必须做 URL 编码否则解析错位charsetutf8mb4不能省否则中文写入报Incorrect string value。host0.0.0.0是为了让同局域网的同学能访问只在本机调试写127.0.0.1也行。3.2 用模型类替代手写 SQL接着定义三张表对应的模型class Student(db.Model): __tablename__ student id db.Column(db.Integer, primary_keyTrue) sno db.Column(db.String(20), uniqueTrue, nullableFalse) name db.Column(db.String(50), nullableFalse) class_name db.Column(db.String(50)) def to_dict(self): return {id: self.id, sno: self.sno, name: self.name, class_name: self.class_name}to_dict方法的作用是把 ORM 对象转成字典jsonify才能序列化。不写这个方法直接返回对象会报Object of type Student is not JSON serializable。__tablename__显式指定表名避免 SQLAlchemy 默认把类名转小写后和已有表对不上。3.3 查询接口与参数校验加一个按班级查学生的接口from flask import request app.route(/students) def list_students(): cls request.args.get(class_name, ).strip() query Student.query if cls: query query.filter(Student.class_name cls) students query.order_by(Student.sno).all() return jsonify([s.to_dict() for s in students])request.args.get拿的是 URL 问号后面的参数比如/students?class_name计算机2101。.strip()去掉前后空格防止前端传了带空格的班级名导致查不到。order_by(Student.sno)按学号排序比按 id 排更符合教务习惯。这里没有做分页两百人以内没问题上千人就要加limit和offset否则一次查询把整表拉进内存。3.4 成绩录入与 MySQL update 语法录入成绩用 POST更新用 PUT对应 SQL 的INSERT和UPDATEapp.route(/score, methods[POST]) def add_score(): data request.get_json() # 先查是否已有该生该课成绩有则更新无则插入 exist Score.query.filter_by( student_iddata[student_id], course_iddata[course_id] ).first() if exist: exist.grade data[grade] else: db.session.add(Score( student_iddata[student_id], course_iddata[course_id], gradedata[grade] )) db.session.commit() return jsonify({ok: True})这段逻辑对应 MySQL 的INSERT ... ON DUPLICATE KEY UPDATE但用 ORM 写更直观。db.session.commit()必须调用否则数据只在会话里进程一退就丢。如果并发量大两个请求同时判断「不存在」然后都插入会撞唯一索引稳妥做法是给(student_id, course_id)加联合唯一约束捕获IntegrityError后转为更新。4. 页面与部署模板渲染、静态资源路径和局域网访问4.1 Jinja2 模板渲染成绩列表Flask 默认从templates目录找模板建templates/scores.html!DOCTYPE html html headmeta charsetutf-8title成绩列表/title/head body table border1 trth学号/thth姓名/thth课程/thth成绩/th/tr {% for row in rows %} tr td{{ row.sno }}/td td{{ row.name }}/td td{{ row.cname }}/td td{{ row.grade }}/td /tr {% endfor %} /table /body /html路由里用render_template传入数据app.route(/view) def view_scores(): rows db.session.query( Student.sno, Student.name, Course.cname, Score.grade ).join(Score, Score.student_id Student.id) \ .join(Course, Course.course_id Course.id).all() return render_template(scores.html, rowsrows)join的顺序不能反先连成绩表再连课程表条件写错会报Unknown column。查询返回的是 Row 对象模板里用属性名访问即可。4.2 静态资源路径错误的排查部署到服务器后最常见的报错是 CSS、JS 加载 404。Flask 默认静态目录是项目根下的static模板里必须用url_for生成路径link relstylesheet href{{ url_for(static, filenamecss/style.css) }}直接写/static/css/style.css在本地能跑但项目挂到子路径比如/score/下就全断。url_for会根据应用配置自动补前缀。如果用了 Nginx 反代还要确认location /static/指向了正确的物理目录否则请求打到 Flask 之前就被 Nginx 拦下返回 404。4.3 局域网访问与端口app.run(host0.0.0.0, port5000)之后同网段的人用http://你的IP:5000访问。Windows 防火墙会弹窗询问是否允许选允许专用网络。如果连不上先在服务器本机curl 127.0.0.1:5000/ping确认服务活着再从另一台机器ping服务器 IP最后查防火墙入站规则。生产环境不要用app.run用waitress-serve --host0.0.0.0 --port5000 app:appWindows 上 waitress 比 gunicorn 兼容性好。5. 避坑与排查五个让项目跑不起来的高频问题5.1 报错Access denied for user rootlocalhost现象启动 Flask 时抛pymysql.err.OperationalError: (1045, Access denied for user rootlocalhost)。 原因连接串里的密码和 MySQL 实际 root 密码不一致或者 MySQL 8.0 装完后 root 只允许auth_socket登录。 解决用mysql -u root -p能登进去说明密码对登不进去就重置能登进去但 Flask 连不上执行ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY 新密码;再FLUSH PRIVILEGES;。5.2 中文写入变成问号或报Incorrect string value现象插入学生姓名「张伟」后查出来是??或者直接报编码错误。 原因建库时没指定utf8mb4或者连接串漏了charsetutf8mb4。 解决ALTER DATABASE score_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;同时确认连接串带 charset 参数。已经建的表要ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4;。5.3RuntimeError: cryptography is required现象PyMySQL 连接 MySQL 8.0 时抛这个错。 原因MySQL 8.0 默认认证插件是caching_sha2_passwordPyMySQL 需要cryptography包做 RSA 加密握手。 解决pip install cryptography写进requirements.txt。不想装就把用户认证插件改成mysql_native_password但不推荐新版本 MySQL 会逐步废弃它。5.4 改了代码页面没变化现象修改模板或路由后刷新浏览器还是旧内容。 原因debugTrue没开或者浏览器缓存了静态文件。 解决开发时确保app.run(debugTrue)模板改动会自动重载静态文件加版本号style.css?v2强制刷新。生产环境关掉 debug用 waitress 重启进程。5.5 外键约束导致删学生失败现象删除学生时报Cannot delete or update a parent row: a foreign key constraint fails。 原因该学生在score表里还有成绩记录外键挡住了删除。 解决先删成绩再删学生或者建表时给外键加ON DELETE CASCADE。教务场景建议先软删除加is_deleted字段标记保留历史数据可追溯。6. 进阶用存储过程算班级排名与加权平均分成绩管理里最绕的不是增删改查是统计。班级排名、加权平均分、补考名单这三样用 Python 循环算也能出结果但数据量一大就慢。把计算下沉到 MySQL 存储过程一次调用返回整张排名表是更省事的做法。先建一个算加权平均分的存储过程DELIMITER // CREATE PROCEDURE class_avg(IN cls VARCHAR(50)) BEGIN SELECT s.sno, s.name, ROUND(SUM(sc.grade * c.credit) / SUM(c.credit), 2) AS weighted_avg FROM student s JOIN score sc ON sc.student_id s.id JOIN course c ON c.id sc.course_id WHERE s.class_name cls GROUP BY s.id ORDER BY weighted_avg DESC; END // DELIMITER ;DELIMITER //是为了让 MySQL 把整个BEGIN...END当成一条语句不然遇到中间的分号就截断了。SUM(sc.grade * c.credit) / SUM(c.credit)就是加权平均学分高的课权重大。ROUND(..., 2)保留两位小数避免前端显示一长串。Flask 里调用app.route(/rank) def rank(): cls request.args.get(class_name, ) result db.session.execute( db.text(CALL class_avg(:cls)), {cls: cls} ) rows [dict(r._mapping) for r in result] return jsonify(rows)db.text里用:cls占位第二个参数传字典SQLAlchemy 会自动转义不要用字符串拼接否则班级名里带引号就是注入漏洞。r._mapping把 Row 转成字典直接dict(r)在新版本 SQLAlchemy 里会报错。验证存储过程是否正确拿一个已知数据的手算结果对一遍比如某班三门课学分分别是 3、2、4成绩 80、90、70加权平均应是(80*390*270*4)/(324) 75.56。跑一遍接口看输出是不是这个数对不上就查JOIN条件是不是漏了课程 id 对应关系。我自己的习惯是每次改完存储过程先在 Workbench 里CALL class_avg(计算机2101);单独跑一遍确认 SQL 本身没问题再去调 Flask 接口。这样出问题时能立刻判断是 SQL 写错还是 Python 传参错省掉来回猜的时间。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

视频卡顿排查指南:3个必问坑点,面试不挂
视频卡顿排查指南:3个必问坑点,面试不挂

视频卡顿排查指南:3个必问坑点,面试不挂 面试被问“视频为什么卡顿”,你只答了“网速慢”?面试官眼神瞬间冷场。这是前端开发中 面试必问… · 2026/9/23 2:07:31

redux-form `Fields` 组件完全指南:用单个连接组件管理多个表单字段
redux-form `Fields` 组件完全指南:用单个连接组件管理多个表单字段

前端UI组件 【免费下载链接】redux-form A Higher Order Component using react-redux to keep form state in a Redux store 项目地址: https://gitcode.com/gh_mirrors/re/redux-form 点击查看 免费下载 Fields 是 redux-form 中与 Field 平级的核心组件&#xf… · 2026/9/23 2:07:31

SARIMA时间序列预测实战:从参数选择到避坑指南
SARIMA时间序列预测实战:从参数选择到避坑指南

简介:针对季节性时间序列预测的MATLAB实现资源,围绕SARIMA(季节性差分自回归滑动平均模型)从数据预处理、平稳性检验、参数选择、模型拟合到预测评估的完整流程展开,适合有统计基础并希望用MATLAB完成季节性数据建模与… · 2026/9/23 2:07:25

3个坑教你一文搞懂网站主编选型
3个坑教你一文搞懂网站主编选型

3个坑教你一文搞懂网站主编选型 看了一堆教程还是不会写项目?别慌,今天咱们不聊虚的,直接上手。 很多初学者卡在“怎么把页面动起来”这一步,以为懂了语法就能干活,结果一到实战就懵圈。 其实,前端框架的选择决定了你后续三年的开发效率,这篇… · 2026/9/23 20:47:09

反转字符串面试必问,这5个坑让你代码跑不通
反转字符串面试必问,这5个坑让你代码跑不通

反转字符串面试必问,这5个坑让你代码跑不通 刚把网上抄的代码丢进 IDE,回车一按,直接报 IndexError 。你盯着屏幕,心里直骂街:这代码明明看着对啊,怎么就炸了? 别慌,这不是你的问题,是这题本身“坑”多。反转字符串是 面试必问… · 2026/9/23 20:47:09

近红外光谱深度学习回归:ConvNet与SpectFormer实战指南
近红外光谱深度学习回归:ConvNet与SpectFormer实战指南

简介:本资源是一套面向科研人员与工程实践者的近红外光谱(NIR)数据回归建模工具包,聚焦深度学习在化学分析、食品检测及农业成分定量等非破坏性检测场景中的落地应用。针对传统统计方法难以处理高维、强噪声光谱数据的痛点&#x… · 2026/9/23 20:46:56

基于YOLOv11的扑克牌识别数据集与训练实战指南
基于YOLOv11的扑克牌识别数据集与训练实战指南

简介:扑克牌识别数据集专为计算机视觉与目标检测任务设计,覆盖A-K共13种牌面字母类别,可支撑扑克牌检测、识别及棋牌类AI应用开发。资源包含1850张原始图像,模型正确识别率可达98.7%,所有图片均已按YOLOv11格式完成标注… · 2026/9/23 20:46:43

3个实操案例一文搞懂dns被篡改原理与排查
3个实操案例一文搞懂dns被篡改原理与排查

3个实操案例一文搞懂dns被篡改原理与排查 官方文档里关于 DNS 解析的章节动辄几十页,参数配置、协议交互、缓存机制堆砌在一起,新手根本抓不住重点,更别提遇到线上域名突然解析到错误 IP 时如何快速定位是 DNS… · 2026/9/23 20:46:43

传送带异物检测:YOLOv11铁棍与垃圾识别实战与避坑指南
传送带异物检测:YOLOv11铁棍与垃圾识别实战与避坑指南

简介:这份传送带异物检测识别数据集面向工业质检、智能矿山与自动化产线场景,适合从事目标检测算法训练与落地的开发者、学生及工程人员使用,可解决传送带运行过程中铁棍、垃圾等异物混入的自动识别问题,为安全预警与分拣提供数据… · 2026/9/23 20:46:36

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码