
简介基于Python的学生宿舍管理系统是一份面向计算机专业毕业设计或课程设计的完整项目源码包围绕入住、退宿、调换宿舍等核心管理流程帮助高校学生快速搭建可运行的在线管理系统。压缩包共185个文件包含36个py后端源码、34个ts和15个vue组成的前端工程、38个svg矢量图标以及png/jpeg图片、less样式、json配置和md文档等整体仅8.03MB结构清晰便于查阅。目前已有505人学习下载适合作为毕设选题参考或课程设计模仿对象。通过学习项目可掌握Flask/Django类开发思路理解MVC分层、前后端分离与数据库表设计源码注释、可运行环境配置和完整目录结构也为论文撰写、系统演示及二次开发提供了直接支撑。若想高效完成学生宿舍管理方向的毕业设计这份资源能节省大量从零搭建的时间是一份实用性较强的完整代码包。1. Python 学生宿舍管理系统的需求与设计边界宿舍管理系统是毕业设计里出现频率最高的选题之一但很多人的实现只停在「增删改查」建几张表、写几个页面把学生和宿舍一对一连起来就收工。答辩时一旦被问到“调宿之后原来的记录怎么留痕”“宿舍满了怎么防止超员分配”项目就撑不住了。真正值得写进课程设计或毕业论文里的是围绕「入住记录」这个事实表建立的状态流转机制——入住、调宿、退宿都是对同一条记录或相邻两条记录的状态变更而不是简单改一个外键字段。本篇按一套可交付的 Python 学生宿舍管理系统来拆解用 Flask SQLAlchemy Jinja2 模板渲染实现覆盖技术选型、数据库设计、核心业务编码、权限控制、本地部署与答辩前验证。内容按“先立理论、再给代码、后讲排错”的顺序展开适合正在做毕业设计或课程设计的人直接参考有几年经验的后端也可以看看调宿状态机与并发校验的设计取舍。2. 技术栈选型与项目结构Python 毕设为什么不用前后端分离2.1 Flask 与 Django 的取舍以及模板渲染的合理性学生宿舍管理系统的并发量、数据量、业务复杂度都远未达到需要微服务或复杂前端工程的程度。毕业设计通常有 2 到 4 周开发时间还要留出写论文和做 PPT 的余量技术栈应该满足三个条件一是自己能讲清楚原理二是答辩环境能稳定运行三是出问题时有足够的社区资料可查。常见做法是选 Flask。Django 自带 Admin 后台和 ORM功能强大但“自带的东西太多”反而让毕设看起来像拼装Flask 核心极小路由、模板、请求上下文都是显式引入的论文里能写的内容更多。前后端分离方案Vue FastAPI 或 Flask REST API看起来更“现代”但对这种内部管理系统来说需要额外处理跨域、Token 刷新、打包部署等与业务无关的问题演示时还容易因为 Node 环境不一致白屏。我一般建议用 Jinja2 模板 Bootstrap 5 CDN 做服务端渲染一个 Flask 进程同时承担页面和接口解压即跑。Python 版本方面毕业设计用 3.10 或 3.11 均可不要追新到 3.13部分依赖可能还没适配。虚拟环境必须用 venv 创建避免把全局 Python 环境弄乱——这是 python 安装教程里最容易被忽略、但在答辩现场最常出问题的环节。2.2 项目目录结构与核心文件职责一个适合作为课程设计交付的 Flask 项目不应该把所有代码塞进单个 app.py。目录结构按蓝本Blueprint拆分既能让代码可读也能在论文的“系统设计”章节里画结构图dormitory_system/ ├── app.py # 应用入口创建 app、注册蓝图 ├── config.py # 配置类数据库连接、密钥、上传路径 ├── requirements.txt # 依赖清单 ├── init_db.py # 建表与初始化管理员账号 ├── models.py # ORM 模型Student/Dormitory/CheckinRecord/AdminUser ├── views/ │ ├── __init__.py │ ├── auth.py # 登录、登出、修改密码 │ ├── student.py # 学生信息管理、入住、调宿、退宿 │ ├── dormitory.py # 宿舍楼栋与房间管理 │ └── report.py # 统计报表入住率、空床位 ├── templates/ # Jinja2 模板 ├── static/ # CSS/JSBootstrap 本地文件或 CDN └── dormitory.db # SQLite 数据库运行时生成models.py放全部 ORM 模型views包中每个模块是一个蓝图。这样做的直接好处是init_db.py可以独立运行建表不需要启动 Web 服务论文里的“系统模块划分”可以直接对应到目录结构答辩时讲代码位置不会手忙脚乱。2.3 最小可运行骨架从空白目录到 Flask 起服务先做一个能跑通的最小版本再逐步加业务。app.py使用应用工厂方式创建实例是 Flask 社区的主流写法也方便后续写单元测试# app.py from flask import Flask, render_template from views.auth import auth_bp from views.student import student_bp from views.dormitory import dormitory_bp def create_app(): app Flask(__name__) app.config.from_object(config.Config) # 读取配置文件 # 注册蓝图每个蓝图负责一组相关路由 app.register_blueprint(auth_bp, url_prefix/auth) app.register_blueprint(student_bp, url_prefix/student) app.register_blueprint(dormitory_bp, url_prefix/dormitory) app.route(/) def index(): return render_template(index.html) return app if __name__ __main__: create_app().run(debugTrue, port5000)config.py中把数据库连接串和 SECRET_KEY 集中管理# config.py import os class Config: BASE_DIR os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI sqlite:/// os.path.join(BASE_DIR, dormitory.db) SQLALCHEMY_TRACK_MODIFICATIONS False SECRET_KEY only-for-course-design # 正式部署必须改为随机值url_prefix让每个蓝图的路由带上模块前缀例如views/student.py里的student_bp.route(/list)实际访问路径是/student/list。SECRET_KEY用于签名 session如果在答辩现场出现“登录后刷新就掉线”的情况多半是这个值在多进程下不一致但单进程 Flask 开发服务器不会遇到。requirements.txt至少包含这些依赖Flask2.3.3 Flask-SQLAlchemy3.1.1 Werkzeug2.3.7以上版本组合是经过大量课程设计项目验证的稳定搭配不要盲目升级到 Flask 3.xFlask-SQLAlchemy的初始化方式有变化网上教程大多基于 2.x遇到报错会浪费大量时间。3. 学生宿舍管理系统的数据库设计入住记录才是事实表3.1 实体关系别把“当前宿舍”直接挂到学生表宿舍管理系统的核心实体有四个管理员、学生、宿舍、入住记录。最常见的错误设计是在student表上加一个dorm_id字段表示“该学生当前住在哪”这个设计在入住和退宿场景下勉强能用但遇到调宿就麻烦了直接更新dorm_id原来的住宿痕迹就丢了如果学生曾经住过 3 个宿舍系统里根本查不到历史。正确做法是把“入住”建模成一条独立记录学生与宿舍之间是多对多的“历史住宿关系”当前状态由记录上的status字段决定。ER 关系如下Student 与 CheckinRecord一对多一个学生可以有多条入住记录但同一时间只能有一条statusliving的记录。Dormitory 与 CheckinRecord一对多一个宿舍同一时刻最多允许capacity条living记录。AdminUser 与业务表无直接外键关系只在操作日志中记录操作人。3.2 核心表结构与建表 SQL三张业务表加一张管理员表的建表语句如下MySQL 8 语法如果是 SQLite 需要把AUTO_INCREMENT改成INTEGER PRIMARY KEY AUTOINCREMENT-- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender ENUM(male, female) NOT NULL, college VARCHAR(100) COMMENT 学院, phone VARCHAR(20), status ENUM(in_dorm, out_dorm) NOT NULL DEFAULT out_dorm ); -- 宿舍表 CREATE TABLE dormitory ( id INT PRIMARY KEY AUTO_INCREMENT, building_no VARCHAR(20) NOT NULL COMMENT 楼栋号, room_no VARCHAR(20) NOT NULL COMMENT 房间号, capacity INT NOT NULL DEFAULT 4 COMMENT 床位总数, used INT NOT NULL DEFAULT 0 COMMENT 已住人数, gender ENUM(male, female) NOT NULL, UNIQUE KEY uk_building_room (building_no, room_no) ); -- 入住记录表业务事实表调宿、退宿都写在这里 CREATE TABLE checkin_record ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, dorm_id INT NOT NULL, bed_no VARCHAR(10) COMMENT 床位号如 A1, status ENUM(living, left) NOT NULL DEFAULT living, checkin_time DATETIME NOT NULL, leave_time DATETIME NULL, operator_id INT COMMENT 操作管理员ID ); -- 管理员表 CREATE TABLE admin_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, role ENUM(super_admin, admin) NOT NULL DEFAULT admin );dormitory.used是一个冗余字段记录当前已住人数。冗余字段在课程设计里是允许的但要注意used的值必须只通过业务代码更新不能在 SQL 里手工改否则会出现“界面显示已满实际数据库里床位空闲”的脏数据。写入时机统一在入住1、退宿-1、调宿旧宿舍 -1 新宿舍 1三个事务里。3.3 关键设计点为什么用 status 字段而不是删除记录checkin_record.status取living/left退宿时只做更新不做删除。好处有四点历史可追溯能回答“某学生大一到大四住过哪些宿舍”统计报表可以直接基于left记录计算入住时长调宿时旧记录置为left、新记录插入为living整个链路不丢数据答辩时老师问“数据删除策略”你可以回答采用逻辑删除这是生产系统的常见做法。为了保障“同一学生同时只有一条 living 记录”可以在应用层加检查但更稳妥的是依赖业务逻辑的锁和事务。索引方面checkin_record(student_id, status)应该建组合索引这是查询频次最高的路径dormitory(building_no, room_no)已由唯一键覆盖。3.3.1 容量校验放应用层还是数据库层床位容量校验通常放在应用层事务内先给宿舍行加锁再比较used与capacity。数据库层也可以加触发器但触发器逻辑在 ORM 之外不容易在论文中展示且排错成本高。对课程设计而言应用层显式判断已经足够。4. 核心业务编码入住、调宿、退宿与权限控制4.1 业务状态机入住记录在什么条件下发生什么迁移把业务规则先写成状态表代码只是对这张表的翻译。这套系统里状态只存在于checkin_record和student两张表场景原状态操作新状态涉及的数据变更新生入住无记录分配床位livingstudent.status 置 in_dormdorm.used 1调宿living迁出原宿舍left旧记录旧宿舍 used -1新宿舍 used 1新记录 living退宿毕业/休学living注销leftstudent.status 置 out_dormdorm.used -1退宿后再次入住left重新分配新 living 记录student.status 置 in_dormdorm.used 1注意调宿不是“改 dorm_id”而是“旧记录终止 新记录开始”两次写入。下面代码用 Flask-SQLAlchemy 实现入住分配# views/student.py from datetime import datetime from flask import Blueprint, request, flash, redirect, url_for from flask_sqlalchemy import SQLAlchemy from models import db, Student, Dormitory, CheckinRecord student_bp Blueprint(student, __name__) db SQLAlchemy() # 实际项目中在 models.py 初始化后导入这里简写 student_bp.route(/int:sid/checkin, methods[POST]) def checkin(sid): student db.session.get(Student, sid) if student.status in_dorm: flash(该学生已在住不能重复入住, danger) return redirect(url_for(student.detail, sidsid)) # 锁定宿舍行防止并发超额入住 dorm (Dormitory.query .filter_by(idrequest.form[dorm_id]) .with_for_update() .first()) if dorm is None or dorm.used dorm.capacity: flash(该宿舍已满或不存在, danger) return redirect(url_for(student.detail, sidsid)) record CheckinRecord( student_idstudent.id, dorm_iddorm.id, bed_norequest.form[bed_no], statusliving, checkin_timedatetime.now(), operator_idsession.get(admin_id) ) dorm.used 1 student.status in_dorm db.session.add(record) db.session.commit() flash(入住成功, success) return redirect(url_for(dormitory.detail, diddorm.id))with_for_update()是行级锁执行SELECT ... FOR UPDATE在事务提交前阻止其他事务修改同一宿舍行从根上解决“两个请求同时抢最后一个床位”的问题。需要注意SQLite 不原生支持该语法如果使用 SQLite 做演示数据库应去掉这一行因为 SQLite 写操作本身是串行的不会出现该并发问题。代码中session来自 Flask 的会话对象用于记录当前登录管理员 ID。4.2 调宿与退宿两条记录的事务性变更调宿的代码逻辑与入住类似但会同时操作两条记录。这里最容易犯的错是分开提交旧记录置left一次 commit新记录插入再一次 commit中间一旦程序异常学生就变成“既没离开旧宿舍也没住进新宿舍”的孤儿状态。student_bp.route(/int:sid/transfer, methods[POST]) def transfer(sid): new_dorm_id request.form[dorm_id] new_bed request.form[bed_no] # 查找当前有效入住记录 old_record (CheckinRecord.query .filter_by(student_idsid, statusliving) .with_for_update() .first()) if old_record is None: flash(该学生当前没有在住记录, danger) return redirect(url_for(student.detail, sidsid)) new_dorm (Dormitory.query .filter_by(idnew_dorm_id) .with_for_update() .first()) if new_dorm.used new_dorm.capacity: flash(目标宿舍已满, danger) return redirect(url_for(student.detail, sidsid)) old_dorm db.session.get(Dormitory, old_record.dorm_id) old_record.status left old_record.leave_time datetime.now() new_record CheckinRecord( student_idsid, dorm_idnew_dorm.id, bed_nonew_bed, statusliving, checkin_timedatetime.now(), operator_idsession.get(admin_id) ) old_dorm.used - 1 new_dorm.used 1 db.session.add(new_record) # 仅添加新记录旧记录通过 ORM 跟踪自动更新 db.session.commit() # 两次修改 一次插入在同一个事务db.session.add(new_record)只需调用一次因为old_record和old_dorm是通过查询加载的托管对象ORM 会在 commit 时自动把它们的字段变更一并提交。调宿的原子性体现在这里任何一步抛异常整个事务回滚数据库不会出现半截状态。退宿更简单找到living记录置为leftstudent.status置out_dormdorm.used减一一次 commit 完成。4.3 权限控制用装饰器实现角色校验宿舍管理系统至少要有两种角色超级管理员可以管理管理员账号普通管理员只能操作学生和宿舍。Flask 里用装饰器实现角色控制最直观# views/auth.py from functools import wraps from flask import session, abort def role_required(role): def decorator(fn): wraps(fn) def wrapper(*args, **kwargs): if not session.get(admin_id): return redirect(url_for(auth.login)) if session.get(role) ! role: abort(403) return fn(*args, **kwargs) return wrapper return decorator student_bp.route(/list) role_required(admin) def student_list(): students Student.query.all() return render_template(student/list.html, studentsstudents)session.get(role)在登录时从数据库读取写入。密码必须用 Werkzeug 的generate_password_hash处理check_password_hash做校验不能明文存储或自己写 MD5。登录校验逻辑如下from werkzeug.security import check_password_hash auth_bp.route(/login, methods[POST]) def login(): username request.form[username] password request.form[password] admin AdminUser.query.filter_by(usernameusername).first() if admin and check_password_hash(admin.password_hash, password): session[admin_id] admin.id session[role] admin.role return redirect(url_for(index)) flash(用户名或密码错误, danger) return redirect(url_for(auth.login))SQLAlchemy 的参数绑定特性会阻止 SQL 注入——filter_by(usernameusername)会把值作为参数传给数据库驱动而不是拼接到 SQL 字符串。答辩时如果老师问“怎么防止 SQL 注入”可以演示把用户名输入为 OR 11 --再观察查询结果这是加分项。4.3.1 日志与排错先看回滚再改代码课程设计阶段最常见的运行时报错是sqlalchemy.exc.OperationalError: database is locked这通常发生在两个进程同时访问 SQLite 数据库比如开着 Flask 又跑了 init_db。解决办法是关闭多余进程或者把数据库换成 MySQL。业务逻辑报错时不要在页面里硬看 500 页面应该切到 Flask 开发服务器的终端输出那里有完整堆栈。5. 部署、初始化数据与答辩前验证拿到课程设计源码压缩包后第一步不是直接双击运行而是按以下顺序启动这套命令适用于 Windows 与 Linuxcd dormitory_system python -m venv venv # Windows: venv\Scripts\activate Linux/macOS: source venv/bin/activate pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple python init_db.py python app.py先建虚拟环境再装依赖避免污染系统 Python。init_db.py会创建dormitory.db并写入默认管理员账号建议默认账号密码就写admin / admin123在答辩现场的 PPT 里展示登录步骤时账号越简单越省时间提交论文前再改掉即可。演示数据的初始化脚本可以在init_db.py中生成避免进入系统后手工录入几十条学生信息# init_db.py 片段 from models import db, Student, Dormitory, AdminUser def seed(): dorm Dormitory(building_no1号楼, room_no101, capacity4, used0, gendermale) db.session.add(dorm) db.session.flush() # 生成 dorm.id students [] for i in range(1, 7): students.append(Student( student_nof202400{i:03d}, namef示例学生{i}, gendermale )) db.session.add_all(students) db.session.commit()db.session.flush()会把 SQL 发送到数据库但未提交此时 ORM 模型能拿到自增主键用于后续外键引用。答辩前二十分钟要用这份数据在浏览器里完整走一遍新建学生、入住、调宿、退宿、查看统计报表确保页面无 500。自检项操作预期结果登录与权限用普通管理员账号访问管理后台403 或隐藏入口超额入住给已满宿舍再分配学生提示“宿舍已满”调宿留痕调宿后查看学生详情显示 2 条记录旧记录为已迁出宿舍容量一致退宿后查看宿舍列表used 减 1且与入住记录数一致最后提醒一个演示现场的高频问题Flask 开发服务器默认只监听127.0.0.1如果评委老师用自己的电脑访问你的演示环境需要用python app.py --host0.0.0.0启动并关闭系统防火墙或放行 5000 端口。另外数据库文件要提前备份答辩前千万不要在演示机器上跑python init_db.py那会清空你精心准备的演示数据只保留初始管理员账号。本文还有配套的精品资源点击获取