Django开发高校选课系统:从设计到优化实践
1. 项目概述与核心需求这个学生选课系统是我去年为一个地方高校开发的教务管理子系统主要解决传统纸质选课流程中的三大痛点选课冲突率高、数据统计滞后、管理效率低下。系统上线后该校的选课冲突率从原来的37%降到了5%以下教务处的排课工作量减少了60%。1.1 系统角色设计系统采用经典的三元权限模型学生端选课/退课、课表查询、成绩查看教师端开课申请、成绩录入、学生名单导出管理员端课程审核、冲突检测、数据统计分析特别注意在实际开发中很多初学者会犯一个错误——把权限判断逻辑写在模板里。正确的做法是在视图层就做好权限验证模板只负责展示。1.2 技术选型考量为什么选择Django而不是Flask主要基于三个实际需求Admin后台教务人员需要快速操作数据库Django Admin开箱即用ORM复杂度选课系统涉及多表联合查询学生-课程-教师-教室安全防护Django自带CSRF、XSS防护适合教育系统敏感数据# 典型的多对多关系模型设计示例 class Student(models.Model): courses models.ManyToManyField(Course, throughEnrollment) class Course(models.Model): teacher models.ForeignKey(Teacher, on_deletemodels.PROTECT) classroom models.ForeignKey(Classroom, on_deletemodels.PROTECT)2. 数据库设计与优化2.1 核心表结构表名关键字段索引设计备注studentid, name, gradeid(主键)学生基本信息courseid, name, capacityid, teacher_id包含选课人数限制enrollmentstudent_id, course_id联合索引记录选课关系schedulecourse_id, time_slotcourse_id时间安排2.2 MySQL优化实践连接池配置# settings.py 配置 DATABASES { default: { ENGINE: django.db.backends.mysql, CONN_MAX_AGE: 300, # 连接保持时间 OPTIONS: { init_command: SET default_storage_engineINNODB, charset: utf8mb4 } } }查询优化技巧使用select_related()减少外键查询次数对高频查询字段建立复合索引避免在循环内执行数据库查询3. 核心功能实现细节3.1 选课事务处理选课操作必须保证原子性这是系统最关键的代码段from django.db import transaction def enroll(student_id, course_id): try: with transaction.atomic(): # 使用select_for_update锁定记录 student Student.objects.select_for_update().get(pkstudent_id) course Course.objects.select_for_update().get(pkcourse_id) # 检查冲突 if Enrollment.objects.filter(studentstudent, course__schedulecourse.schedule).exists(): raise ConflictError(时间冲突) # 检查容量 if course.enrollment_set.count() course.capacity: raise CapacityError(课程已满) # 创建选课记录 Enrollment.objects.create(studentstudent, coursecourse) except Exception as e: # 记录日志并返回友好错误 logger.error(f选课失败: {e}) raise3.2 并发控制方案在高并发选课场景下比如热门课程开放时我们采用三级防护数据库层面行级锁select_for_update应用层面Redis分布式锁前端层面防重复提交机制4. 典型问题与解决方案4.1 选课冲突检测常见问题学生选了同一时间段的不同课程 解决方案在数据库设计时添加联合唯一约束class Schedule(models.Model): course models.OneToOneField(Course, on_deletemodels.CASCADE) weekday models.PositiveSmallIntegerField() # 1-7表示周一到周日 time_slot models.CharField(max_length20) # 如1-2节 class Meta: unique_together (classroom, weekday, time_slot)4.2 性能优化记录问题现象选课高峰期系统响应变慢排查过程使用Django Debug Toolbar发现N1查询问题数据库监控显示锁等待时间过长Redis监控发现热点Key最终方案重写查询使用prefetch_related对课程表进行分片存储引入读写分离架构5. 部署与监控5.1 生产环境配置推荐使用Docker Compose部署version: 3 services: web: image: nginx:alpine ports: - 80:80 volumes: - ./static:/static depends_on: - app app: build: . command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 environment: - DJANGO_SETTINGS_MODULEconfig.settings.prod volumes: - ./logs:/app/logs db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORDsecurepassword volumes: - db_data:/var/lib/mysql redis: image: redis:alpine5.2 监控指标必须监控的四个关键指标选课API的99线延迟数据库连接池使用率活跃事务数量Redis缓存命中率6. 源码结构说明完整项目包含以下核心模块/src ├── config/ # Django配置 ├── apps/ │ ├── accounts/ # 用户认证 │ ├── courses/ # 课程管理 │ ├── enrollment/ # 选课逻辑 │ └── reporting/ # 统计报表 ├── static/ # 静态文件 ├── templates/ # 前端模板 └── utils/ # 公共工具在开发过程中我发现Django的transaction.atomic装饰器在嵌套使用时会有意外行为这导致我们早期版本出现过数据不一致的问题。后来我们统一采用context manager方式管理事务并在所有写操作接口添加了详细的操作日志。