简介:这是一套基于Python Django与Vue技术栈的兴趣班预约管理系统毕业设计资源包,面向计算机相关专业学生及需要课程设计、工程实训或初期项目立项的开发者。系统划分管理员、教师、学生三种角色:管理员负责教师、学生、课程、公告等信息的增删改查与批量操作;教师可查看自身课程并审核学生的预约与取消申请;学生能搜索课程、提交含预约时长与原因的预约、查看审核状态并取消预约,功能链路完整,适合作为毕设或大作业参考。资源包共735个文件,约19.91MB,涵盖39个py后端源码、41个vue前端组件、53个css与164个js样式脚本、2个sql数据库文件,以及docx、doc等说明文档,另附安装、运行与构建批处理脚本,便于快速部署调试。目前已有1891人学习下载,可帮助读者掌握Django与Vue前后端分离开发、MySQL数据建模及角色权限控制的完整实现思路。
1. 从一份毕设源码说起:Django 兴趣班预约管理系统到底解决什么问题
每到学期初,培训机构的报名窗口前排起长队,教务老师拿着 Excel 反复核对名额,家长在微信群里追问“还有没有位置”——这是我见过最典型的兴趣班管理现场。这套基于 Django 的兴趣班预约管理系统,核心就是把“课程发布、名额控制、在线预约、名单导出”这条链路从人工搬到 Web 端。它适合三类人:正在找 Python 毕设题目的学生、想用真实项目练 Django 的入门开发者、以及需要一套可二次开发的教务工具的小型机构。标题里的 Django、Python、预约管理系统、毕设源码、sql 这几个词,对应的正是技术栈、业务场景和交付物。下面我按“能跑起来、能改得动、能讲清楚”的顺序,把这份源码拆开讲。
2. 技术选型与数据库设计:为什么是 Django 而不是 Flask
2.1 选 Django 的三个现实理由
做预约类系统,绕不开三件事:用户认证、后台管理、表单校验。Flask 需要自己拼扩展,Django 自带 admin、auth、form 三大件,对毕设场景来说能省掉至少一周的脚手架时间。具体到这套系统:
- 认证:Django 内置 User 模型,直接支持学员/教师/管理员三种角色的权限划分,不用从零写登录态。
- 后台:admin 后台改几行代码就能管理课程、时段、预约记录,答辩演示时很直观。
- ORM:预约系统涉及大量关联查询(某课程某时段还剩几个名额),Django ORM 的
annotate和aggregate比手写 SQL 更不容易出错。
常见做法是用AbstractUser扩展用户表,加一个role字段区分身份。我一般会这样定义:
# models.py from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): ROLE_CHOICES = ( ('student', '学员'), ('teacher', '教师'), ('admin', '管理员'), ) role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='student') phone = models.CharField(max_length=11, blank=True) class Meta: db_table = 'sys_user'这里db_table显式指定表名,方便和 sql 文件里的建表语句对齐。role字段是后续所有权限判断的基础,不要用is_staff硬凑,否则教师和管理员会混在一起。
2.2 核心表结构与 sql 文件对照
毕设交付的 sql 文件通常包含建表和初始数据。这套系统的核心表大概五张,字段设计直接决定预约逻辑能不能跑通:
| 表名 | 关键字段 | 作用 |
|---|---|---|
| sys_user | id, username, password, role, phone | 用户与角色 |
| course | id, name, teacher_id, capacity, description | 课程信息 |
| schedule | id, course_id, start_time, end_time, quota | 开课时段与名额 |
| booking | id, user_id, schedule_id, status, create_time | 预约记录 |
| notice | id, title, content, publish_time | 公告 |
booking表的status字段建议用整型枚举:0 待确认、1 已确认、2 已取消。用字符串存状态在 sql 查询时容易写错,而且占空间。schedule表的quota是剩余名额,每次预约成功要减一,取消要加一,这个加减操作必须放在事务里,否则并发时会超卖。
2.3 预约名额的原子扣减
超卖是预约系统最经典的坑。假设两个学员同时点“预约”,都读到 quota=1,都判断通过,结果两个人都预约成功,名额变成 -1。解决办法是用数据库层面的原子更新:
# views.py from django.db import transaction from django.db.models import F @transaction.atomic def book_schedule(request, schedule_id): # select_for_update 锁行,防止并发读 schedule = Schedule.objects.select_for_update().get(id=schedule_id) if schedule.quota <= 0: return JsonResponse({'code': 1, 'msg': '名额已满'}) # F 表达式在数据库层做减法,避免先读后写 Schedule.objects.filter(id=schedule_id).update(quota=F('quota') - 1) Booking.objects.create( user=request.user, schedule=schedule, status=0 ) return JsonResponse({'code': 0, 'msg': '预约成功'})select_for_update必须在事务内使用,它会对该行加排他锁,第二个请求会阻塞到第一个事务提交。F('quota') - 1把减法交给数据库执行,避免 Python 层读到的旧值覆盖。注意 MySQL 要确认存储引擎是 InnoDB,MyISAM 不支持行锁,这个方案会失效。
3. 从零跑通项目:环境搭建与最小可运行路径
3.1 Python 与依赖安装的版本选择
标题里带了 Python 毕设源码,实际部署时版本对不上是高频翻车点。Django 3.2 支持 Python 3.6 到 3.9,Django 4.x 要求 Python 3.8 以上。如果 sql 文件里用了utf8mb4字符集,MySQL 要 5.7 以上。我一般这样配:
# 创建虚拟环境,避免污染全局 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS/Linux 激活 source venv/bin/activate # 安装依赖,版本按源码 requirements.txt 来 pip install django==3.2.18 pip install mysqlclient==2.1.1 pip install pillow==9.5.0mysqlclient在 Windows 上编译经常报错,替代方案是装pymysql并在__init__.py里加pymysql.install_as_MySQLdb()。pillow是 ImageField 的依赖,课程封面图会用到。如果源码里没有 requirements.txt,按报错逐个装即可,不要一次性装最新版,Django 4 和 3 的 URL 路由写法有差异。
3.2 数据库配置与 sql 文件导入
拿到 sql 文件后,先建库再导入,不要直接改 Django 的 migration:
# 登录 MySQL 建库 mysql -u root -p CREATE DATABASE interest_class DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入 sql 文件 mysql -u root -p interest_class < interest_class.sql然后在settings.py里配置连接:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'interest_class', 'USER': 'root', 'PASSWORD': '你的密码', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } }导入后执行python manage.py migrate --fake-initial,让 Django 认为表已存在,避免重复建表报错。如果 sql 文件里的表结构和 models.py 不一致,以 sql 文件为准,改 models.py 去适配,因为毕设答辩时数据库设计文档通常和 sql 文件对应。
3.3 启动与静态文件处理
python manage.py runserver 0.0.0.0:8000如果页面样式丢失,检查settings.py里的STATIC_URL和STATICFILES_DIRS。开发模式下 Django 自动服务静态文件,但DEBUG=False时需要配 Nginx 或whitenoise。毕设演示保持DEBUG=True即可,但要注意ALLOWED_HOSTS加上'*'或本机 IP,否则局域网访问会被拒绝。
4. 预约业务逻辑实现:从选课到取消的完整链路
4.1 课程列表与时段筛选
学员进入系统后,第一步是看到可选课程和对应时段。视图层用 ORM 做关联查询:
# views.py def course_list(request): # prefetch_related 减少查询次数,避免 N+1 courses = Course.objects.prefetch_related('schedule_set').all() data = [] for c in courses: schedules = c.schedule_set.filter(start_time__gte=timezone.now()) data.append({ 'id': c.id, 'name': c.name, 'teacher': c.teacher.username, 'schedules': [ {'id': s.id, 'start': s.start_time.strftime('%Y-%m-%d %H:%M'), 'quota': s.quota} for s in schedules ] }) return JsonResponse({'code': 0, 'data': data})prefetch_related会把关联的 schedule 一次性查出来,避免循环里每条课程都查一次数据库。start_time__gte=timezone.now()过滤掉已过期的时段,这个条件不加的话,学员会看到历史课程,体验很差。
4.2 预约与取消的状态流转
预约成功后生成booking记录,状态为 0。教师或管理员确认后改为 1,学员取消改为 2。取消时要归还名额:
@transaction.atomic def cancel_booking(request, booking_id): booking = Booking.objects.select_for_update().get(id=booking_id, user=request.user) if booking.status == 2: return JsonResponse({'code': 1, 'msg': '已取消,请勿重复操作'}) booking.status = 2 booking.save() # 归还名额 Schedule.objects.filter(id=booking.schedule_id).update(quota=F('quota') + 1) return JsonResponse({'code': 0, 'msg': '取消成功'})这里先判断状态再操作,防止重复取消导致名额虚增。select_for_update同样不能省,否则并发取消和预约可能把 quota 算错。
4.3 教师端名单导出
教师需要看到某时段的预约名单,导出 Excel 是常见需求。用openpyxl生成:
from openpyxl import Workbook from django.http import HttpResponse def export_booking(request, schedule_id): schedule = Schedule.objects.get(id=schedule_id) bookings = Booking.objects.filter(schedule=schedule, status=1).select_related('user') wb = Workbook() ws = wb.active ws.append(['学员姓名', '手机号', '预约时间']) for b in bookings: ws.append([b.user.username, b.user.phone, b.create_time.strftime('%Y-%m-%d %H:%M')]) response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename=booking_list.xlsx' wb.save(response) return responseselect_related('user')把用户信息一起查出来,避免导出时逐条查用户表。文件名用英文,中文文件名在某些浏览器会乱码。
5. 避坑与排查:这套源码最容易翻车的五个地方
5.1 现象:登录后跳转 403,提示 CSRF 验证失败
原因:Django 的 CSRF 中间件要求 POST 请求带csrfmiddlewaretoken,前端用 Ajax 时没带或者模板里没加{% csrf_token %}。
解决:模板表单里加{% csrf_token %};Ajax 请求从 cookie 读csrftoken放到 header 的X-CSRFToken。如果调试阶段想临时关闭,注释掉settings.py里的CsrfViewMiddleware,但上线前必须恢复。
5.2 现象:预约成功但名额没减,或者减了两次
原因:没用事务,或者F表达式和save()混用。比如先schedule.quota -= 1再schedule.save(),并发时会覆盖。
解决:统一用update(quota=F('quota') - 1),并且整个预约逻辑包在transaction.atomic里。检查 MySQL 引擎是否为 InnoDB。
5.3 现象:sql 文件导入报错 “Unknown character set: utf8mb4”
原因:MySQL 版本低于 5.5.3,不支持 utf8mb4。
解决:升级 MySQL,或者把 sql 文件里的utf8mb4替换为utf8。但utf8存不了 emoji,如果课程名有特殊符号会截断。
5.4 现象:python manage.py migrate报 “Table already exists”
原因:sql 文件已经建了表,Django 的 migration 记录表django_migrations里没有对应记录,再次 migrate 会重复建表。
解决:用--fake-initial参数,让 Django 跳过已存在的表。或者手动在django_migrations表里插入记录。
5.5 现象:静态文件 404,页面没有样式
原因:DEBUG=False时 Django 不再服务静态文件,或者STATICFILES_DIRS路径写错。
解决:开发阶段保持DEBUG=True;部署时用python manage.py collectstatic收集到STATIC_ROOT,再由 Nginx 指向该目录。
6. 二次开发与验证:让这套系统真正能用在机构里
6.1 加一个“候补排队”功能
名额满了之后,学员只能看到“已满”,体验不好。加候补逻辑:预约失败时写入waiting_list表,有人取消时按排队顺序通知。核心代码:
# 取消时触发候补 def cancel_booking(request, booking_id): # ... 前面的取消逻辑 ... # 查候补第一个人 next_wait = WaitingList.objects.filter(schedule_id=booking.schedule_id, notified=False).order_by('create_time').first() if next_wait: next_wait.notified = True next_wait.save() # 这里可以发站内信或邮件 return JsonResponse({'code': 0, 'msg': '取消成功'})order_by('create_time')保证先来先得,notified字段防止重复通知。这个功能在答辩时是加分项,因为它体现了对真实业务的理解。
6.2 用 Django shell 验证数据一致性
改完代码后,别急着点页面,先用 shell 查一遍:
python manage.py shellfrom app.models import Schedule, Booking from django.db.models import Count, F # 检查有没有 quota 为负的时段 bad = Schedule.objects.filter(quota__lt=0) print(bad) # 检查预约数和名额消耗是否对得上 for s in Schedule.objects.all(): confirmed = Booking.objects.filter(schedule=s, status=1).count() print(s.id, s.quota, confirmed)如果quota为负,说明并发扣减有问题;如果confirmed大于初始名额,说明超卖。这两个检查我每次改完预约逻辑都会跑一遍,比点页面靠谱。
6.3 参数调优与部署建议
CONN_MAX_AGE:数据库连接复用,设为 60 秒可以减少连接开销,但要注意 MySQL 的wait_timeout要大于这个值。ATOMIC_REQUESTS:设为 True 可以让每个请求自动包在事务里,但会影响性能,预约接口单独用transaction.atomic更可控。- 分页:课程列表和预约记录都要分页,
Paginator每页 10 到 20 条,数据量大了之后不分页会拖慢响应。
这套源码的价值不在于代码多复杂,而在于它覆盖了“用户-课程-时段-预约”这条完整链路,改一改就能变成健身房约课、实验室预约、会议室预定。我自己的习惯是拿到任何毕设源码,先跑通登录和一条核心业务,再去看数据库表关系,最后才动 UI。这样即使代码写得乱,也能快速定位问题。希望帮到你。
本文还有配套的精品资源,点击获取