Django宿舍管理系统课程设计:ORM建模与查询优化实战

发布时间:2026/9/16 15:45:47
Django宿舍管理系统课程设计:ORM建模与查询优化实战 简介基于Python Django框架开发的学生宿舍管理系统配套完整数据库脚本面向高校计算机专业学生以及正在准备课程设计、期末大作业的开发者。项目已获导师指导并通过最终以97分高分结题核心业务功能、依赖环境与初始数据均已配置妥当解压后即可直接部署运行无需二次修改。资源包共862个文件整体大小约30.65MB涵盖84个Python后端逻辑文件、103个前端组件、30个HTML页面、多份SQL初始化脚本同时包含容器部署配置、服务运行参数、接口文档组件与富文本编辑组件等前后端结构分明方便对照理解Django框架的项目分层、接口设计与数据表关联。目录组织清晰宿舍管理、学生信息、楼栋分配等模块均可快速定位既能作为可运行的高分项目使用也能帮助读者梳理完整的Web开发实现思路。目前已有247人学习下载适合需要拿高分Django全栈项目范例进行参考或二次开发的同学。1. 学生宿舍管理系统这道课程设计难点不在 CRUD很多人的“宿舍管理系统”就是几张表配上增删改查交上去能跑答辩时却经不起三个问题空床位怎么统计、分配为什么不均衡、删数据时关联记录怎么办。这套基于 Python 和 Django 的课程设计之所以能拿高分不是因为界面多漂亮而是把宿舍管理中真正烦人的需求落在了数据模型和 ORM 上并且把“有源码有数据库”做成了可交付的状态而不是临时拼凑的演示稿。这篇博文不是给你一段贴上去就能跑的运行说明而是按一个完整课程设计的做法拆开讲宿舍楼的分层、床位分配、报修流程、统计查询、Admin 后台定制、性能排查以及答辩验收前怎么把数据造得够漂亮。目录规模我会按下述结构组织只讲你实际能用到的部分。2. Django 宿舍管理系统的数据模型先想清楚表再谈源码2.1 宿舍楼的层级关系怎么建模一个宿舍管理系统的核心无非是“楼栋 - 楼层 - 房间 - 床位 - 学生 - 入住记录”。很多课程设计把房间和床位直接混在同一张表里结果调整床位时要改好几处答辩时被追问就只能支支吾吾。正确做法是把层级拆开至少分成宿舍楼、宿舍房间、学生、入住记录四张核心表。这里的建模是有讲究的宿舍楼和房间是一对多关系房间和学生是一对多关系但“学生入住”这件事本身应该单独用一条记录表示因为同一个学生可能在学期中换宿舍历史记录要保留。下面是一个常见的 models.py 结构注释已经标清了每个字段的作用from django.db import models from django.contrib.auth.models import User class DormBuilding(models.Model): name models.CharField(楼栋名称, max_length50, uniqueTrue) address models.CharField(楼栋地址, max_length100, blankTrue) floor_count models.PositiveSmallIntegerField(楼层数, default6) def __str__(self): return self.name class DormRoom(models.Model): building models.ForeignKey(DormBuilding, on_deletemodels.CASCADE, verbose_name所属楼栋) room_number models.CharField(房间号, max_length20) capacity models.PositiveSmallIntegerField(可住人数, default4) # 床位总数 gender models.CharField(性别限制, max_length1, choices[(M, 男), (F, 女)]) class Meta: unique_together (building, room_number) # 同一楼栋房号唯一上面 DormRoom 里没有单独建床位表因为对大多数课程设计来说房间容量就是床位总数入住记录里的人数就是已占数。如果你要支持“床号”维度再加一张 Bed 表即可但这会让代码量翻倍分数不一定涨先把层级讲清楚更重要。2.2 models.py 里最容易加分的字段设计接下来是学生和入住记录。学生表不建议直接继承 Django 的 AbstractUser那样会把用户系统和学生信息耦合等你要批量导入 Excel 时会很难受。常见做法是单独写一个 Student 模型用 OneToOneField 关联到 User 作为登录账号不关联也能跑看你要不要做登录。class Student(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, nullTrue, blankTrue) student_no models.CharField(学号, max_length20, uniqueTrue) name models.CharField(姓名, max_length30) department models.CharField(学院, max_length50) major models.CharField(专业, max_length50) phone models.CharField(联系电话, max_length11, blankTrue) def __str__(self): return f{self.student_no} {self.name} class CheckInRecord(models.Model): student models.ForeignKey(Student, on_deletemodels.PROTECT, verbose_name学生) room models.ForeignKey(DormRoom, on_deletemodels.PROTECT, verbose_name宿舍房间) checkin_date models.DateField(入住日期, auto_now_addTrue) checkout_date models.DateField(退宿日期, nullTrue, blankTrue) is_active models.BooleanField(是否当前有效, defaultTrue)字段设计里最容易被忽略的是is_active。筛选某一时刻住在某房间的人不要去统计 CheckInRecord 记录条数而是直接过滤is_activeTrue。on_deletemodels.PROTECT也有讲究如果学生有入住记录就不允许直接删学生这比默认的 CASCADE 更符合教务现实——学生如果换过宿舍删除学生会导致历史记录消失答辩时这是加分项。2.3 从模型到数据库迁移的命令和交付物规范模型写完之后下面这套命令是从零开始最标准的流程# 在项目根目录执行创建 Django 项目和应用 django-admin startproject dormitory manage.py startapp dormitory_app # 在 settings.py 的 INSTALLED_APPS 里加入 dormitory_app 后执行迁移 python manage.py makemigrations dormitory_app python manage.py migrate # 创建管理员账号用于进入 admin 后台 python manage.py createsuperuser迁移执行后数据库里会自动生成十几张表包括 Django 自带的 session、auth 相关表以及我们上面定义的四张业务表。课程设计里的“数据库”指的不是一张孤立的 .sql 文件而是这套迁移文件加上一个能从头复现环境的完整工程。我一般建议把db.sqlite3保留一份同时把migrations/目录里的迁移文件完整提交这样才能算真正的“源码数据库”。3. 宿舍分配与组合查询把 ORM 用出“业务逻辑”感3.1 贪心分配按学院和班级优先凑满楼层宿舍分配是整个系统里最能体现“业务逻辑”的地方也是答辩时最能展示编程思维的部分。最常见的需求是同学院、同专业的学生优先住同一区域尽量安排在同一栋楼不要出现一个学院的学生分散在七八个楼的情况。这本质上是一个带约束的分组问题。下面我用一个贪心算法来实现优先把同一专业的学生分到同一房间房间满了再进下一间专业内的人不够填满一个房间时就跨专业凑满。这个算法不追求全局最优但速度快、实现直观、演示效果好。from itertools import groupby from django.db.models import Count def assign_rooms(room_batch, student_batch): 贪心分配先按专业分组专业内顺序分配一个房间装满后开下一个 room_batch: 指定一组宿舍房间 QuerySet需预排序 student_batch: 待分配学生 QuerySet按专业排好序 返回分配结果列表 result [] room_iter iter(room_batch) room next(room_iter) current_count 0 for student in student_batch: # 房间已满则换下一间 if current_count room.capacity: room next(room_iter) current_count 0 # 分配记录 CheckInRecord.objects.create(studentstudent, roomroom, is_activeTrue) result.append((student, room)) current_count 1 return result调用前要把学生按专业排序并且只把尚未分配的学生传进来。这里几行代码就是完整的分配逻辑不依赖复杂的第三方库。缺陷也很明显如果某个专业人数特别多后面的专业会离得很远但课程设计的场景下这个复杂度够了。如果你想拿分更高可以加一句说明“这是贪心的解换成运筹学里的二分图匹配可以做全局最优”老师就会知道你有扩展思维。3.2 aggregate Case/When 统计宿舍空位宿舍系统最常做的统计是“哪些楼还有空位”“每栋楼入住率是多少”。新手最容易写成这样把所有房间全部取出来在 Python 里循环计算。# 不推荐循环里发 SQL房间数量一多就慢 rooms DormRoom.objects.all() for room in rooms: used CheckInRecord.objects.filter(roomroom, is_activeTrue).count()如果房间只有几十间这样写没感觉但如果一个校区有几千间宿舍每间房一条 count SQL就是几千次数据库往返。正确写法是使用 Django 的 ORM 聚合函数一次性查出每个房间的已住人数和空床位数from django.db.models import Count, Case, When, IntegerField, Sum room_stats DormRoom.objects.annotate( used_countCount(checkinrecord, filterQ(checkinrecord__is_activeTrue)), empty_countCount(checkinrecord) * 0 # 占位下面用 capacity 减 )上面这种 Count 写法有 bug因为 Count 会去重当房间没有任何入住记录时Count 结果恒为 1。更好的写法是直接数入住表再关联room_stats DormRoom.objects.annotate( used_countCount( checkinrecord, filterQ(checkinrecord__is_activeTrue) ) ).annotate( empty_countF(capacity) - F(used_count) ).filter(empty_count__gt0)这里用到了Count的 filter 参数以及F表达式完成 SQL 层的减法计算。最终empty_count大于零的就是还有空位的房间再把它聚合到楼栋维度就能看到每栋楼的入住情况。3.3 删除操作的坑Queryset.delete() 与 instance.delete()Django 的删除看起来只是一个调用但里面有两个答辩高频坑。第一个坑是批量删除不触发模型的delete()方法无论你重写这个方法来清理日志还是回写状态Queryset.delete()都会直接绕过去走 SQL 级删除。第二个坑是默认的级联删除删掉一栋楼会导致该楼所有房间、入住记录全部被 CASCADE一旦误操作数据就全没了。# 正确删除单个学生时判断是否有入住记录 def safe_delete_student(student_id): student Student.objects.get(pkstudent_id) has_record CheckInRecord.objects.filter(studentstudent).exists() if has_record: raise ValueError(该学生存在入住记录请先办理退宿再进行删除) student.delete()推荐在 admin 后台里把delete_selected动作重写或者干脆在模型里用on_deletePROTECT挡一层。装配了 PROTECT 之后删除操作会抛出 ProtectedError需要你显式去处理这比让数据悄悄消失要安全得多。4. 视图、路由与 Admin 后台让课程设计“看起来像产品”4.1 FBV 还是 CBV课程设计怎么选宿舍管理系统的页面不多无非是学生列表、房间列表、入住登记、报修列表。常见做法是用 FBV函数视图因为逻辑直白你读起来不绕。答辩时老师问“你这个代码是怎么走的”FBV 能从头到尾讲清楚而 CBV 一上来就是 get/post/queryset 的继承链你极可能把自己讲晕。不过为了显得你不只会写过程式可以在用到表单校验的接口上用一点 DRF 序列化或者把查询接口做成 JSON 返回让前端用简单模板渲染。下面是一个典型的房间查询视图from django.http import JsonResponse from django.contrib.auth.decorators import login_required from django.db.models import Count, Q login_required def room_list_api(request): building_id request.GET.get(building_id, ) rooms DormRoom.objects.filter(building_idbuilding_id) if building_id else DormRoom.objects.all() rooms rooms.annotate( usedCount(checkinrecord, filterQ(checkinrecord__is_activeTrue)) ).values(id, room_number, capacity, used) result list(rooms) for item in result: item[empty] item[capacity] - item[used] return JsonResponse({code: 0, data: result})这个视图做了一件事允许前端按楼栋 ID 过滤房间列表并在数据库查询阶段就算出已住人数一次 SQL 返回所有房间的空位信息。参数说明building_id是查询参数不传就查全部Count上加了 filter 条件只统计有效入住记录。返回值里的empty是前台直接可以用于展示的字段。4.2 Admin 定制list_filter/search_fields/readonly_fields 的组合Django Admin 是课程设计里的“隐藏分系统”。如果你什么都不改后台就是一个原始的数据管理页面老师看了无感但稍微配置一下就能体现出你对 admin 机制的理解。最值得改的是 DormRoomAdmin 和 StudentAdminfrom django.contrib import admin from .models import Student, DormRoom admin.register(DormRoom) class DormRoomAdmin(admin.ModelAdmin): list_display (building, room_number, capacity, gender, current_count) list_filter (building, gender) search_fields (room_number,) list_per_page 20 def current_count(self, obj): return obj.checkinrecord_set.filter(is_activeTrue).count() current_count.short_description 当前入住人数 admin.register(Student) class StudentAdmin(admin.ModelAdmin): list_display (student_no, name, department, major, phone) search_fields (student_no, name, major) list_filter (department,)list_display里混了一个方法是可以的它会在每一行实时计算入住人数。如果宿舍数量多这里会有 N1 查询问题后面我会给出优化方案但至少从功能上是完整的。search_fields可以传多个字段Django 会自动拼接 Q 对象做 LIKE 查询。4.3 把报修流程做成 Admin 可直接处理的工作台报修是宿舍管理系统中最能体现“流程感”的功能。大多数课程设计只做报修单的增删改查状态字段只有“已提交/已处理”。要加分就把状态变成更真实的生命周期状态键状态名说明submitted已提交学生填写维修单刚入库processing处理中维修人员接单finished已完成处理完等待学生确认rejected已驳回填单有误或不在维修范围在 Admin 中你可以用list_editable直接把状态变成下拉框在列表页就能改不用进详情页。再把actions注册一个批量完成操作这样“一次性把多张维修单标记为已处理”只需要勾选、下拉、点执行。from django.contrib import admin from .models import RepairOrder admin.action(description将选中报修单标记为已完成) def mark_as_done(modeladmin, request, queryset): queryset.update(statusfinished, finish_timetimezone.now()) admin.register(RepairOrder) class RepairOrderAdmin(admin.ModelAdmin): list_display (order_no, student, room, type, status, create_time) list_filter (status, type) list_editable (status,) actions (mark_as_done,)这里绕开了 model 的 save 方法直接执行queryset.update好处是只发一条 UPDATE SQL缺点是不会走 save 的自动逻辑所以在finish_time上必须手动设置。Django 后台定制到这里基本就达到了“课程设计里能展示的性价比最高操作”你只需要在答辩时现场演示一次批量更新即可。5. 查询性能与部署排错答辩时被问最多的几个问题5.1 N1 查询与 django-debug-toolbar 的定位方法当你的宿舍列表页显示每一行都要查一次入住人数时N1 查询就会出现。第一次 QuerySet 取列表算 1之后逐行 count 又是 N 次。肉眼感知不出来的原因是数据量小但老师很可能会问“如果宿舍有 1 万间怎么办”。正确答法是用django-debug-toolbar抓出重复 SQL再做查询优化。pip install django-debug-toolbar安装后在settings.py里加入debug_toolbar到 INSTALLED_APPS在urls.py里挂载路由。打开宿舍列表页后你会在页面右侧看到 SQL 面板里面每条 SQL 会出现多次一模一样的语句这就是 N1 的证据。5.2 select_related 与 prefetch_related 该怎么选N1 的解决方案取决于外键关系的方向。如果查询入口在 DormRoom 表需要展示其关联的 building 信息这是正向外键用select_related就够了rooms DormRoom.objects.select_related(building).annotate(...)如果要从房间查入住记录或者从学生查多张入住记录这是反向关联且结果集是多条需要用prefetch_related做预处理避免逐条去查子表。对于上面的 Admincurrent_count计算方法我的优化方式是直接预取聚合值而不在模板里反复 countfrom django.db.models import Count, Q rooms DormRoom.objects.annotate( used_countCount(checkinrecord, filterQ(checkinrecord__is_activeTrue)) ) for room in rooms: print(room.used_count)这里用一个带条件和 SQL 别名的小技巧把“当前有效入住人数”作为一个虚拟字段挂在每条记录上模板直接取room.used_count。数据量越大这种方式与逐条 count 的差距越明显。答辩时把这两段代码并排摆出来讲基本就没有死角。5.3 迁移后 admin 样式丢失、时区、ALLOWED_HOSTS 与宝塔部署Django 课程设计里最容易翻车的是部署环节。本地跑得好好的传到服务器后 Admin 页面没 CSS排错半天发现是STATIC_ROOT没有配置。常见做法是在settings.py里这样处理STATIC_URL /static/ STATIC_ROOT BASE_DIR / staticfiles # 部署时执行 collectstatic 收集到这里的目录在服务器上执行python manage.py collectstatic --noinput然后在 Nginx 配置里把/static/指向这个绝对路径。宝塔面板部署 Django 时需要在网站设置里选择 Python 项目指定入口文件wsgi.py和项目根目录特别注意的是settings.py中的DEBUG False以及ALLOWED_HOSTS必须填上域名或服务器 IP否则会直接抛 DisallowedHost 错误。时间字段也有一个经典坑如果不设置USE_TZ False数据库里存的是带时区的 UTC 时间你在 Admin 里看到的时间会比北京时间少 8 小时。我的做法是开发时保持默认展示时统一用模板过滤器localtime这样逻辑上更标准不会被老师挑出“时区是什么”的问题。6. 答辩验收前的最后一步造数据、测指标、写报告6.1 用 bulk_create 造一批逼真的验收数据答辩演示最怕数据太少点开空空的列表毫无说服力。手动录入几百条不现实应该用bulk_create批量生成。下面这段代码可以放在一个临时脚本里跑一次就能造出 300 名学生和 80 间宿舍的入住数据from django.utils import timezone from datetime import timedelta import random students [] for i in range(300): students.append(Student( student_nof2025{i:04d}, namef学生{i}, departmentrandom.choice([信息学院, 机械学院]), majorrandom.choice([软件工程, 计算机科学, 自动化]), )) Student.objects.bulk_create(students, batch_size100)批量写入的要点是batch_size它不是 MySQL 显式批量代替逐条插入而是控制每批插入多少条防止一次拼接的 SQL 过长。配合这个脚本你还可以写一个循环把学生按专业顺序往房间里塞整个演示数据十分钟就能造完。6.2 指标口径与报告写法课程设计报告里必然有“系统功能”和“数据库设计”两章但很多人的报告只是一张张截图堆砌。更容易拿分的做法是写清楚统计口径比如“入住率 当前有效入住记录数 / 宿舍总床位数”再选择三个指标做可视化宿舍楼入住率、各学院已分配学生人数、报修完成率。只画直方图和饼图即可不要为了炫技去用花哨的图表。6.3 演示动线的最后安排答辩演示的顺序建议不要从登录页开始而是直接打开后台宿舍楼列表先展示 filter 筛选和搜索再切换到分配脚本演示一次自动分配最后打开 debug_toolbar 的 SQL 面板说一句“这里是预加载优化的效果”。如果你能做到以上任意一步至少在完成度上已经超过了一般的课程设计作品。本文还有配套的精品资源点击获取