
简介这份资源是面向高校学生与Python初学者的课程设计完整项目包以田径运动会管理系统为主题帮助读者在真实场景中练习Python编程与数据库管理技术。系统覆盖运动员信息管理、比赛项目维护、成绩录入与查询、名次排名展示等核心业务适合作为课程设计参考、自学练手或答辩演示素材。压缩包共35个文件、约11.24MB包含4个py源码文件与7个pyc编译文件另有1个sql建库脚本、3个xlsx数据表、15张png界面截图以及spec、sln、pyproj等工程配置文件和exe可执行程序结构上分为数据库操作、图形界面、业务逻辑与错误处理等模块便于按功能拆解学习。目前已有280人学习下载。读者可从中获得完整的数据库表设计与SQL建表思路、基于GUI的交互实现方式、成绩统计与排名计算逻辑以及项目工程化组织与打包发布的参考范例对理解Python与SQLite协作开发有较高实用价值。1. 从一份课程设计压缩包说起田径运动会管理系统到底管什么每年校运会前一周体育组最头疼的不是报名而是把几十个班级交上来的纸质报名表汇总成一张能直接排赛程的总表。这份「Python课程设计田径运动会管理系统源码数据库.zip」解决的正是这个场景用 Python 写一套带图形界面的桌面程序把运动员、项目、报名、成绩、积分这几张表管起来数据落到本地数据库里跑在一台普通办公电脑上就能用。它适合两类人——正在找课程设计题目的学生以及想拿一个完整增删改查项目练手 Python 数据库编程的入门者。整套东西的核心不是界面多花哨而是数据库表设计得对不对、增删改查逻辑写得顺不顺这也是后面几章要拆开讲的重点。2. 先想清楚数据怎么存五张表撑起整个系统很多人拿到源码第一反应是双击运行看界面结果一改数据就报错根子往往在表结构没吃透。田径运动会的业务其实很收敛一个运动员可以报多个项目一个项目可以有多名运动员成绩挂在「报名」这条关系上积分再根据名次算出来。把这层关系翻译成表五张就够。2.1 核心表结构与字段设计下面这套表结构是我在课程设计里反复用过的版本字段名尽量直白方便新手对照源码读。表名作用关键字段athlete运动员信息id、name、gender、class_name、student_nosport_item比赛项目id、item_name、item_type、unit、recordenrollment报名关系id、athlete_id、item_id、heat_noresult成绩记录id、enrollment_id、score、rank_no、is_finalscore_rule积分规则id、rank_no、pointsenrollment是整张网的中心它把运动员和项目连起来result又挂在enrollment上这样同一个运动员在不同项目里的成绩互不干扰。score_rule单独抽出来是为了让「第一名几分」这种规则能改不用动代码。2.2 用 SQLite 建库并写入初始数据课程设计用 SQLite 最省事一个.db文件拷走就能交。下面这段脚本负责建表并塞几条测试数据可以直接抄。import sqlite3 # 连接数据库文件不存在会自动创建 conn sqlite3.connect(sports_meet.db) cur conn.cursor() # 运动员表student_no 加唯一约束防止重复录入 cur.execute( CREATE TABLE IF NOT EXISTS athlete ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, gender TEXT CHECK(gender IN (男,女)), class_name TEXT, student_no TEXT UNIQUE ) ) # 项目表item_type 区分径赛/田赛unit 记录成绩单位 cur.execute( CREATE TABLE IF NOT EXISTS sport_item ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_name TEXT NOT NULL, item_type TEXT, unit TEXT, record REAL ) ) # 报名表athlete_id item_id 组合唯一避免同一人重复报同一项 cur.execute( CREATE TABLE IF NOT EXISTS enrollment ( id INTEGER PRIMARY KEY AUTOINCREMENT, athlete_id INTEGER, item_id INTEGER, heat_no INTEGER, UNIQUE(athlete_id, item_id), FOREIGN KEY(athlete_id) REFERENCES athlete(id), FOREIGN KEY(item_id) REFERENCES sport_item(id) ) ) # 成绩表rank_no 存名次is_final 标记是否已确认 cur.execute( CREATE TABLE IF NOT EXISTS result ( id INTEGER PRIMARY KEY AUTOINCREMENT, enrollment_id INTEGER, score REAL, rank_no INTEGER, is_final INTEGER DEFAULT 0, FOREIGN KEY(enrollment_id) REFERENCES enrollment(id) ) ) # 积分规则表 cur.execute( CREATE TABLE IF NOT EXISTS score_rule ( id INTEGER PRIMARY KEY AUTOINCREMENT, rank_no INTEGER, points INTEGER ) ) # 初始化积分规则前八名分别记 9,7,6,5,4,3,2,1 rules [(1,9),(2,7),(3,6),(4,5),(5,4),(6,3),(7,2),(8,1)] cur.executemany(INSERT OR IGNORE INTO score_rule(rank_no, points) VALUES (?,?), rules) conn.commit() conn.close() print(数据库初始化完成)逻辑上先建被引用的表再建引用表顺序不能反否则外键约束会报错。UNIQUE(athlete_id, item_id)这行是血泪经验——没有它同一个运动员能被重复报进同一个项目后面算积分直接翻倍。is_final字段是留给「成绩录入后还要复核」这个环节的课程设计里常被忽略但真用起来很关键。参数上gender用CHECK约束限定取值比在界面层校验更可靠record存项目纪录单位跟unit走径赛存秒、田赛存米不要混着存字符串。3. 把增删改查写扎实报名、成绩、积分三条主线界面再好看底层还是这几条 SQL。课程设计答辩时老师最爱问的就是「你这个查询怎么写的」所以这一章把三条主线的实现讲透。3.1 报名管理插入前先查重报名本质是往enrollment插一条记录但插入前要确认运动员和项目都存在且没重复报。def add_enrollment(conn, athlete_id, item_id, heat_noNone): cur conn.cursor() # 先查是否已报名避免触发唯一约束异常 cur.execute(SELECT id FROM enrollment WHERE athlete_id? AND item_id?, (athlete_id, item_id)) if cur.fetchone(): return False, 该运动员已报名此项目 # 再确认运动员和项目都存在 cur.execute(SELECT id FROM athlete WHERE id?, (athlete_id,)) if not cur.fetchone(): return False, 运动员不存在 cur.execute(SELECT id FROM sport_item WHERE id?, (item_id,)) if not cur.fetchone(): return False, 项目不存在 cur.execute(INSERT INTO enrollment(athlete_id, item_id, heat_no) VALUES (?,?,?), (athlete_id, item_id, heat_no)) conn.commit() return True, 报名成功先查后插虽然多一次查询但比直接插、靠捕获异常来判断要清晰得多新手也容易调试。heat_no是分组道次径赛预赛分道用得上田赛可以传None。3.2 成绩录入与名次自动计算成绩录入后要按项目排名次再根据名次查积分规则。这里用一条UPDATE配合子查询完成排名。def calc_rank(conn, item_id): cur conn.cursor() # 按成绩排序径赛成绩越小越好田赛越大越好 cur.execute(SELECT item_type FROM sport_item WHERE id?, (item_id,)) row cur.fetchone() if not row: return order ASC if row[0] 径赛 else DESC # 用窗口函数或子查询给每个成绩打名次 cur.execute(f SELECT r.id, r.score FROM result r JOIN enrollment e ON r.enrollment_id e.id WHERE e.item_id ? ORDER BY r.score {order} , (item_id,)) records cur.fetchall() for idx, (rid, _) in enumerate(records, start1): cur.execute(UPDATE result SET rank_no? WHERE id?, (idx, rid)) conn.commit()order根据项目类型动态切换这是田径系统区别于普通成绩系统的关键点——径赛比谁快田赛比谁远排序方向相反。名次从 1 开始enumerate别用下标直接当名次否则第一名变成 0。3.3 积分统计一条 GROUP BY 出班级总分团体总分是校运会最关心的输出用一条聚合查询就能算出来。def class_total_score(conn): cur conn.cursor() cur.execute( SELECT a.class_name, SUM(sr.points) AS total FROM result r JOIN enrollment e ON r.enrollment_id e.id JOIN athlete a ON e.athlete_id a.id JOIN score_rule sr ON r.rank_no sr.rank_no WHERE r.is_final 1 GROUP BY a.class_name ORDER BY total DESC ) return cur.fetchall()WHERE r.is_final 1只统计已确认的成绩避免录入中途的脏数据影响总分。GROUP BY后面跟的字段必须和SELECT里非聚合字段一致这是 SQL 的基本要求但新手常在这里翻车。4. 避坑与排查课程设计里最容易翻车的五个点这一章按「现象 → 原因 → 解决」写都是我在帮人调这类系统时真实遇到过的。现象一程序换台电脑就打不开报找不到数据库文件。原因代码里写的是绝对路径比如D:\code\sports.db换机器路径不存在。 解决统一用相对路径把.db文件和脚本放同一目录用os.path.join(os.path.dirname(__file__), sports_meet.db)拼路径。现象二删除运动员后他的报名记录还在查成绩时报外键错误。原因建表时没开外键约束或者删除时没做级联处理。 解决SQLite 默认不启用外键连接后要执行PRAGMA foreign_keys ON删除运动员前先删对应的enrollment和result或者建表时加ON DELETE CASCADE。现象三成绩排序结果不对径赛第一名排到了最后。原因排序方向写死成DESC没按项目类型区分。 解决像 3.2 那样先查item_type径赛用ASC田赛用DESC。现象四同一个运动员能重复报同一个项目积分算出来偏高。原因enrollment表没加唯一约束或者界面层没做查重。 解决建表时加UNIQUE(athlete_id, item_id)插入前再查一次双保险。现象五中文姓名存进去变成乱码。原因数据库连接没指定编码或者文件保存编码不是 UTF-8。 解决Python 3 默认 UTF-8但 SQLite 连接时确认文件本身编码源码文件统一存成 UTF-8别用 GBK。提示改表结构后记得把旧的.db文件删掉重新生成否则CREATE TABLE IF NOT EXISTS不会更新已有表。5. 让系统更像样导出成绩单和几个实用技巧课程设计只要求增删改查但答辩时能多做一个导出功能印象分完全不一样。用 Python 自带的csv模块就能把班级总分导成表格不需要装额外库。import csv def export_score(conn, filenameclass_score.csv): rows class_total_score(conn) # newline 防止 Windows 下多出空行 with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([班级, 总分]) writer.writerows(rows) print(f已导出到 {filename})encodingutf-8-sig这个细节值得记一下带 BOM 的 UTF-8 能让 Excel 直接双击打开不乱码不带的话中文会变问号。newline是 Windows 平台写 CSV 的必备参数少了它每行之间会多一个空行。再补两个让代码更稳的习惯。一是所有数据库操作包在try...except里出错时rollback别让半截数据留在库里try: cur.execute(INSERT INTO athlete(name, gender, class_name, student_no) VALUES (?,?,?,?), (张三, 男, 高一(1)班, 2024001)) conn.commit() except sqlite3.IntegrityError as e: conn.rollback() print(学号重复或数据非法, e)二是把常用查询封装成函数界面层只负责调函数、显示结果别把 SQL 散落在按钮回调里。我见过太多课程设计一个按钮回调里塞了七八条 SQL改一个字段要翻半天。把数据访问单独放一个db.py界面放ui.py结构清楚老师一看就知道你懂分层。最后说个验证方法造一组边界数据——同一个班报满 8 个项目、一个项目只有 1 个人报名、成绩相同需要并列名次——跑一遍看积分对不对。并列名次这块简单做法是名次相同积分取平均或者按规则表里靠前的名次给分具体看你们学校规程。我一般会先跟体育老师确认规则再写代码不然算错了返工更麻烦。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。