Python课程设计:田径运动会管理系统源码深度解析

发布时间:2026/9/10 11:14:59
Python课程设计:田径运动会管理系统源码深度解析 简介这是一份Python课程设计田径运动会管理系统源码与数据库打包面向需要完成数据库或Python课程设计的高校学生提供了一套可直接运行的运动会管理项目。压缩包共35个文件、约11.24MB包含4个.py源码、1个.sql数据库脚本、3个.xlsx运动员及成绩数据表另附可直接运行的exe客户端、pyc编译文件与15张功能界面截图覆盖登录、信息查询、成绩录入、项目分组、名次排名等核心模块。目前已有280人学习下载。项目源码清晰分离了数据库操作、Tkinter图形界面与业务逻辑三个部分可学习如何设计运动员表、项目表、成绩表和排名表之间的关联以及成绩录入、排名计算等功能的实现配合演示Excel与Visual Studio工程文件便于快速运行、调试和二次开发是课程设计展示和Python数据库实战入门的理想资料。1. 从登录框到成绩单这套课设代码最值钱的是数据链路接手一套课程设计代码最怕的不是界面丑而是答辩时被问到成绩排名怎么算出来的删掉一个运动员之后成绩表会怎样这类数据链路问题就卡壳。田径运动会管理系统这套源码把最常见的业务场景完整走了一遍登录需要账号表校验运动员信息要支持编号和姓名两种查询方式录入成绩要同时校验运动员表和项目表名次不能手填而是由排序逻辑生成最后还要按学院出汇总报表。从压缩包里的文件列表可以清晰看出这是个分层项目——DBclass.py 负责数据库操作、GUI.py 负责界面交互、client.py 是启动入口、createCD.sql 是建表脚本旁边还配了 Excel 示例数据和已打包好的 client.exe。正在找 Python 课程设计参考的人可以拿着它直接跑通流程想把这套骨架改成仓库管理、图书管理等其他场景的开发者也能从表结构和统计逻辑里直接复用经验。2. 文件结构反推架构DBclass 负责连接GUI 负责交互2.1 源码包里的文件到底在做什么解压压缩包之后先别急着双击 exe把文件和目录过一遍架构图基本就出来了。这个项目的文件组织非常典型是一个入口 — 界面 — 数据层三件套结构我把关键文件整理成了下面的对应关系。文件职责推断判断依据client.py程序启动入口有同名 client.spec说明打包入口是它GUI.py图形界面与事件绑定截图里的登录界面、功能选择界面都对应这里的窗口类DBclass.py数据库连接与 SQL 封装类名直接是 DB提供查询和写入方法account_information_entry.py管理员账号信息入口登录界面和账号表的校验逻辑会调用它createCD.sqlMySQL 建库建表脚本.sql 后缀包含建库和 create table 语句运动员信息.xlsx运动员基础数据示例界面添加运动员信息和批量导入共用这个格式运动员成绩.xlsx成绩数据导出模板成绩单导出时按这个结构写 Exceldemo.xlsx演示用数据集空库时导入它可以直接演示全部功能client.exe已打包的可执行文件PyInstaller 产物运行不需要安装 Python登录失败.png / 查无此人.png异常分支截图说明项目里做了失败提示不是只画了主流程这里有一个容易被忽略的细节__pycache__里全部是.cpython-310.pyc说明源码是在 Python 3.10 环境下编译运行的。如果你本机是 3.8 或者 3.11直接跑 client.py 一般没问题但如果重新打包 exe建议统一到 3.10避免第三方依赖在不同版本下行为不一致。2.2 DBclass.py 里的数据库连接设计数据层是整套系统的地基。虽然文件名只是简单的 DBclass但它承担了连接管理、SQL 执行、事务提交三件事。常见做法是用 PyMySQL 连接 MySQL连接参数集中写在类初始化方法里。import pymysql class DB: def __init__(self, hostlocalhost, userroot, password123456, databasetrack_meet): self.conn pymysql.connect( hosthost, useruser, passwordpassword, databasedatabase, charsetutf8mb4, cursorclasspymysql.cursors.DictCursor ) def query(self, sql, argsNone): with self.conn.cursor() as cursor: cursor.execute(sql, args) return cursor.fetchall() def execute(self, sql, argsNone): with self.conn.cursor() as cursor: affected cursor.execute(sql, args) self.conn.commit() return affected四个连接参数是最容易踩坑的地方host 默认写 127.0.0.1 只能连本机 MySQL客户机上如果数据库跑在另一台服务器这里要改成对应 IPpassword 要和本地 MySQL 实际密码一致课设代码里默认的 123456 十有八九和你环境对不上database 名称必须与 createCD.sql 里的建库语句同名否则报Unknown databasecharset 建议锁死 utf8mb4否则导入 Excel 里的中文姓名会乱码。query和execute两个方法的返回值设计值得学习查询返回字典列表写入返回受影响行数。界面层判断到底插进去没有时直接看 execute 的返回值大于 0 才弹添加成功比 try-except 硬扛要清晰得多。这套封装对课程设计来说刚好既不臃肿又能应付增删改查。2.3 GUI.py 与业务逻辑的耦合GUI.py 用的是 Tkinter这是 Python 自带的标准库不需要额外安装第三方界面框架。它的逻辑是拿到 db 对象把数据库操作直接嵌在按钮回调里典型写法如下import tkinter as tk from tkinter import ttk, messagebox from DBclass import DB class MainWindow: def __init__(self, db): self.db db self.root tk.Tk() self.root.title(田径运动会管理系统) self.keyword tk.StringVar() def search_athlete(self): keyword self.keyword.get().strip() sql (SELECT no, name, college FROM athlete WHERE no LIKE %s OR name LIKE %s) rows self.db.query(sql, (f%{keyword}%, f%{keyword}%)) if not rows: messagebox.showwarning(提示, 查无此人) else: self.show_result(rows)界面层直接用%s占位符做参数化查询把用户输入当作参数传给 execute而不是拼进 SQL 字符串这个习惯挡住了 SQL 注入。截图里会看到编号或姓名.png和姓名查询.png两张图对应的就是LIKE匹配的两条路径——输入纯数字时可以走编号精确匹配输入中文时走姓名模糊匹配界面共用一个输入框底层 SQL 用 OR 连接。需要注意 Tkinter 的 StringVar 要在变量绑定控件前创建否则控件拿不到字符串变量的引用输入框内容永远是空字符串。很多课设代码在输入框里怎么输都查不到结果问题往往出在这一行。3. createCD.sql 里的表关系与名次计算路径3.1 四张表撑起一个运动会打开 createCD.sql核心是四张业务表的设计。我按字段职责把它们拆开看关系非常清楚。表名关键字段作用accountaccount_no, password管理员登录账号athleteno, name, college, gender运动员基本信息eventevent_no, event_name比赛项目字典表scoreid, athlete_no, event_no, score, rank成绩记录与名次运动员表和项目表是字典数据成绩表是关系数据。一条成绩记录必须同时关联一个运动员和一个项目所以 score 表里 athlete_no 和 event_no 都设置成外键。CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, athlete_no VARCHAR(20) NOT NULL, event_no INT NOT NULL, score DECIMAL(8, 2) NOT NULL, rank INT DEFAULT NULL, FOREIGN KEY (athlete_no) REFERENCES athlete(no), FOREIGN KEY (event_no) REFERENCES event(event_no) );score 字段用 DECIMAL(8,2) 而不是 FLOAT是因为田径成绩有大量精确到百分之一秒的径赛数据FLOAT 在反复比较时会产生浮点误差DECIMAL 是定点数排序和相等判断都稳定。rank字段允许为空在成绩尚未录入完时保留 NULL只有排名计算跑过之后才填充。外键约束的意义在于录入成绩时如果运动员编号不存在数据库直接拒绝写入这比在 Python 里先查一次再插入要可靠得多。3.2 成绩录入为什么要做项目分组校验界面截图里有一张输入运动员参加的项目编号.png这个设计就是为了校验运动员和项目的对应关系。一个运动员可以报名多个项目但一个运动员在同一个项目里只能有一条成绩否则排名统计会出现重复数据。所以插入成绩时不能只插 athlete_no 和 event_no还要先查一遍运动员是否报名该项目。SELECT COUNT(*) FROM registration WHERE athlete_no %s AND event_no %s;如果没有 registration 表退一步的做法是在 score 表上建联合唯一索引用数据库兜底保证同一运动员同一项目只能有一条成绩记录。这个索引在成绩录入场景下还有一个附带好处按运动员查成绩单、按项目排名次都走这个索引查询速度会明显快于全表扫描。3.3 名次计算一条 UPDATE 搞定窗口函数排序名次是管理系统里最容易做成反面教材的功能。很多课程设计里是查到成绩后在 Python 里排序循环再逐条 UPDATE 回数据库数据量小的时候看不出问题一旦同一项目有几百条成绩循环更新既慢又容易在中间出错。正确做法是把排名计算放到数据库里用一条 UPDATE 加上窗口函数完成。UPDATE score s JOIN ( SELECT athlete_no, event_no, RANK() OVER (PARTITION BY event_no ORDER BY score ASC) AS rk FROM score ) t ON s.athlete_no t.athlete_no AND s.event_no t.event_no SET s.rank t.rk;这段 SQL 的逻辑核心是RANK() OVER (PARTITION BY event_no ORDER BY score ASC)。PARTITION BY 表示按项目编号分组每个项目内部独立排名ORDER BY score ASC 表示成绩数值越小排名越靠前适用于径赛短跑这类用时少者胜的项目。如果是跳远、铅球这类田赛项目成绩越大越好把 ORDER BY 改成 DESC 即可。RANK()与DENSE_RANK()的差别在于并列名次两个并列第一之后RANK 会跳过第二名直接排第三DENSE_RANK 则不会跳。对运动会场景来说 RANK 更合理因为沉甸甸的奖牌榜里没有第二名位置空缺的说法。要注意的是窗口函数要求 MySQL 8.0 及以上版本如果是 5.7 就得用用户变量来模拟分组排名逻辑会绕一些。3.4 学院成绩汇总的聚合查询截图里有学院成绩.png和输入学院信息.png对应的是按学院统计总分的报表页面SQL 落在两表 JOIN 加分组聚合上。SELECT a.college, COUNT(DISTINCT s.athlete_no) AS athlete_count, SUM(s.score) AS total_score FROM athlete a JOIN score s ON a.no s.athlete_no GROUP BY a.college ORDER BY total_score DESC;这里有两个容易出错点。第一COUNT 必须加 DISTINCT 限定到 athlete_no因为一个运动员可能参加多个项目产生多条成绩记录直接 COUNT(*) 会把总人数统计成总参赛人次。第二SUM(s.score) 只对已录入的成绩生效如果某个运动员缺考没有成绩记录JOIN 结果里根本不会出现他学院总分自然不包含缺考项。业务上如果要求缺考按 0 分计算就得先 LEFT JOIN 再对 NULL 做 IFNULL 处理。4. 登录鉴权、信息查询、成绩录入的完整流程拆解4.1 登录失败不是界面问题是账号表没初始化压缩包里有一张登录失败.png的截图这是相见恨晚的一张图。登录失败最常见的原因不是密码输错而是 account 表里根本没有数据。createCD.sql 建完表之后是空表你要先插入一条管理员记录登录界面才有账号可验证。INSERT INTO account (account_no, password) VALUES (admin, admin123);登录界面在真实的代码里要做两层判断第一层是输入框非空校验两个输入框任一个为空就直接提示账号或密码不能为空不发起数据库查询第二层才查库比对账号和密码。比对时注意密码字段不要以明文形式展示在数据库中课程设计级别可以用 hashlib 做一次 SHA-256 散列再存储。import hashlib raw_password admin123 hashed hashlib.sha256(raw_password.encode(utf-8)).hexdigest()账号密码校验通过之后界面才会销毁登录窗口创建功能选择主窗口。这个流程对应截图里登录界面.png到功能选择界面.png的跳转关系。4.2 编号或姓名查询的两种检索路径编号或姓名.png和姓名查询.png两张截图暴露了查询功能的两种入口在实现上共用同一个查询方法靠 SQL 参数不同区分路径。如果输入是纯数字优先走编号精确匹配包含中文就按姓名模糊匹配。实际上用一个 OR 条件就能同时覆盖两个入口。def search_athlete(self, keyword): sql (SELECT no, name, college, gender FROM athlete WHERE no %s OR name LIKE %s) rows self.db.query(sql, (keyword, f%{keyword}%)) return rows值得注意的细节是编号字段no在数据库里通常定义成 VARCHAR 而不是 INT因为运动员编号可能带有前缀或前导零用 INT 类型会把 001 存成 1查询时前端传 001 反而匹配不上。这是一个典型的看着能跑但数据不对的坑课程设计代码里保留字符串类型说明作者踩过这个坑。4.3 成绩录入界面与运动员成绩单的联动成绩录入界面.png和运动员成绩单.png之间的关系是录入和查询两条通路在数据层的交汇。录入成绩时界面会提供运动员选择下拉框和项目选择下拉框选完以后自动带入运动员编号避免手工输入编号产生的格式错误。实际代码里两个下拉框的数据源是两个独立的数据库查询athletes self.db.query(SELECT no, name FROM athlete) events self.db.query(SELECT event_no, event_name FROM event)下拉框显示中文名存储值绑定编号这是 GUI 开发里最常见的显示值与存储值分离模式。Tkinter 的 ttk.Combobox 通过一个字典实现这个映射选中运动员名字时拿到的是 athlete_no 而不是姓名文本。成绩录入完点保存后Treeview 表格马上刷新新纪录出现在运动员成绩单列表的底部这个即时反馈依赖插入成功后重新执行一次 SELECT而不是手动拼接界面数据。4.4 成绩修改与删除的级联约束策略成绩录错了要改运动员退赛要删这两件事都涉及 score 表的级联处理。score 表的外键如果设置了ON DELETE CASCADE删除运动员时数据库会把他的所有成绩一并删除应用层不需要写任何历史清理代码。FOREIGN KEY (athlete_no) REFERENCES athlete(no) ON DELETE CASCADE这对运动会管理系统是合理的策略运动员信息删除后保留他过去的成绩没有业务意义。但要注意报表页面最好对级联删除给用户一个二次确认因为成绩一旦跟着删除学院统计的总分也会立刻变化。界面上对应的做法是删除按钮回调里先弹 messagebox.askyesno确认后才调用 DELETE 语句。5. 用 PyInstaller 打包成 exe 与二次改造的三种做法5.1 client.spec 透露的打包细节压缩包里的 client.spec 是 PyInstaller 生成的打包配置文件它决定了 exe 的入口、依赖和窗口属性。最简单的打包命令是下面这行在 client.py 同级目录执行即可。pyinstaller client.specspec 文件里的核心配置决定了产物形态a Analysis( [client.py], pathex[.], hiddenimports[pymysql, openpyxl], noarchiveFalse, ) exe EXE( pyz, a.scripts, exclude_binariesTrue, name田径运动会管理系统, consoleFalse, )consoleFalse 是最容易忽略的参数之一。课程设计演示时如果控制台窗口和 GUI 窗口一起弹出来观感很差设成 False 可以只保留 GUI。调试时反过来把它临时改成 TruePyMySQL 连接报错、未捕获异常都会打印在黑窗口里定位问题比瞎猜快得多。hiddenimports 里列出 pymysql 和 openpyxl 很关键。PyInstaller 静态分析 import 语句时对通过字符串动态导入的模块会漏掉。pymysql 的 connections 模块和 openpyxl 的 reader 模块都是典型的动态导入目标不写进 hiddenimports打包出的 exe 运行到一半会报 ModuleNotFoundError而且只在实际触发数据库连接或 Excel 导出的那一刻才暴露。5.2 运行时报错的排查路径拿到源码先别急着改功能按下面的顺序把环境跑通报错概率降一半。先看 MySQL 服务有没有启动Windows 下用net start mysql或服务管理器确认。用命令行客户端测试连接mysql -u root -p密码不对就先用 root 登录改掉 DBclass.py 里的 password 参数。登录 MySQL 后执行source createCD.sql建表注意建库语句里的库名必须和 DBclass.py 的 database 参数严格一致。插入初始管理员账号否则登录界面永远弹登录失败。双击 client.exe 前先在命令行运行client.exeWindows 下 GUI 程序的报错信息会被吞掉命令行里跑能看到 traceback。双击 exe 没反应是最常见的打包问题八成是上面第二步或者第四步没做完。命令行跑一下错误信息会直接告诉你连不上数据库还是账号表为空。5.3 把课设改成仓库管理系统的换皮思路这套骨架并不只属于田径运动会。它的表结构本质是主表 记录表 统计表的通用模式改造成其他管理系统的操作路径是从数据层往上换皮。第一层改库。把 athlete 表改成 student 表或者 goods 表把 no、name、college 三个字段换成业务对应的编号、名称、所属分类event 表对应物料分类或课程列表score 表对应库存变动或选课记录。第二层改 SQL。排名窗口函数换成库存汇总的SUM(quantity) GROUP BY goods_no学院成绩统计换成按部门汇总的金额统计。第三层改界面。把 GUI.py 里的 Tkinter Label 文本和 Treeview 列名替换成新业务名词项目分组下拉框的查询语句把SELECT event_no FROM event改成SELECT category_id FROM category其余绑定逻辑原样保留。换完以后用 demo.xlsx 造 100 条假数据从登录到统计完整走一遍。能稳定输出结果说明这套骨架已经接住了新业务。课程的答辩重点是排名计算的窗口函数和 Excel 导入的联动逻辑这两块是数据库课程设计里最能体现能力深度的位置讲清楚比堆界面有意义得多。本文还有配套的精品资源点击获取