资讯详情

基于Django的中小学课后延时服务系统开发实践

📅 2026/10/22 9:28:30 | 华诺云谱 👁 阅读
基于Django的中小学课后延时服务系统开发实践
孩子放学早、家长下班晚中间的“延时空白”一直是双职工家庭的心病。学校推出课后延时服务后选课、排班、考勤、费用核算全靠人工登记几个班还好全校几十个班一起运转教务老师天天忙到焦头烂额还容易出错。这时候用Python搭配Django框架搭建一套中小学课后延时服务系统就能把课程发布、学生选课、教师考勤、课时费统计这些环节全部串起来让教务从表格堆里解放出来。这篇文章我会从项目拆解、技术选型、核心代码实现一直聊到上线部署和踩坑记录适合正在做课程设计、毕业设计或者真有这类管理系统需求的人参考。1. 项目需求与功能拆解1.1 教务管理场景里的角色痛点先还原一下真实使用场景。课后延时服务涉及四类人学校教务管理员、任课老师、学生和家长。教务要维护课程目录、安排上课时间、生成选课名单老师要查看自己负责的课程、上课签到、记录学生考勤学生和家长要浏览可选课程、完成报名、查看孩子出勤记录。如果靠微信群接龙加Excel统计选课冲突、漏报错报、课时费对不上账都是家常便饭。我自己接过一个模拟项目X需求方起初只说“做一个能报名和统计的系统”结果一聊才发现要处理的问题远不止报名这一件事。比如同一个学生同一时间段只能选一门课同一个老师同一时间段不能排两门课请假和补课怎么记录按课时还是按次数结算费用这些全是需求的一部分。所以正式动手前一定要把角色和业务规则梳理清楚否则代码写一半就要返工。1.2 功能模块划分与优先级一个完整的系统至少要有六个模块用户认证、课程管理、选课中心、考勤管理、费用统计、公告通知。用户认证可以用Django自带的User模型做扩展加一个role字段区分身份不搞独立的注册入口而是由管理员创建账号后发给老师和家长。课程管理维护课程名称、授课老师、上课时间、上课地点、课程费用、最大人数。选课中心面向学生展示可选课程提交选课后需要校验时间冲突和人数限制。考勤管理是老师端的功能上课前对已选课学生进行签到系统自动记录到课状态。费用统计则是按学生汇总课时数、按老师汇总课时费最后生成报表。优先级上先把选课流程跑通再补考勤和统计。因为选课涉及并发冲突是整个系统最核心的业务逻辑考勤偏记录类操作逻辑简单但字段设计要留足余地费用统计可以直接依赖考勤数据前置数据准确了后面才能算得清。1.3 业务流程里的状态流转延时服务业务有一个典型的生命周期课程创建 - 开放选课 - 学生报名 - 满员/截至 - 开课考勤 - 结束归档。每个节点都要有状态标记。课程状态我习惯用“草稿、报名中、已截止、进行中、已结束”五个状态管理状态切换通过定时任务或手动操作触发。选课记录状态用“已报名、已取消、已签到、缺勤、补课”区分这样教务和财务各环节都清晰。实际开发中状态字段不能只存一个文本最好用IntegerField加CHOICES比如STATUS_CHOICES ((0, 草稿), (1, 报名中), ...)。这种写法既节省存储也方便代码里做判断。后面做统计时一句filter(status4)就能拿到所有要结算的课程非常顺手。2. 技术选型与架构设计2.1 为什么选Django而不是Flask或FastAPI单看课后延时服务这个场景功能集中在多表关联、权限控制、后台管理、报表生成Django几乎是零思考成本的选择。Flask借口的自由度很大但用户认证、ORM、Admin后台、Form校验这些都要自己搭或者选第三方库开发周期明显拉长。FastAPI优势在异步高性能但一个校内管理系统并发量通常不会很高为了异步引入一堆生态反而增加维护成本。Django的Admin后台对教务场景来说是个杀手级功能。课程表、学生表、选课表直接用Admin注册改几行代码就能让教务在线增删改查无需专门开发管理界面。前台选课页面单独用模板和视图写后台管理交给Admin一套框架全搞定省一半工作量。这里补一个基于常见实践的选型版本对比框架开发效率自带ORM后台管理适合场景Django高有功能强大自带Admin数据模型复杂、业务流转单一Flask中需配置SQLAlchemy无需自行开发轻量接口、功能简单FastAPI中高需配置SQLAlchemy或Tortoise无高并发API、前后端分离2.2 MTV架构与项目目录规划Django的MTVModel-Template-View架构特别适合这种业务型系统。Model层定义数据表和业务规则Template层渲染页面View层写业务逻辑。我通常按模块拆Django的app而不是把全部功能塞进一个app里。比如建立一个users app处理用户和角色courses app处理课程与选课attendance app处理考勤与请假stats app处理统计报表。实际目录我偏好这样组织after_school/ ├── manage.py ├── config/ # 项目配置settings、urls、asgi ├── apps/ │ ├── users/ # 用户、角色、学生档案 │ ├── courses/ # 课程、选课、班级关联 │ ├── attendance/ # 签到、请假、补课记录 │ └── stats/ # 统计报表、费用汇总 ├── templates/ # 公共模板 ├── static/ # CSS、JS、图片 └── media/ # 用户上传文件如课程封面这种按业务边界拆app的方式后面加新功能只需要新增app或往已有app里加模块不会动到其他业务代码改起来也容易定位。2.3 数据库模型设计要点课后延时系统的核心表有这几张课程表Course、选课表Enrollment、考勤表Attendance、学生档案StudentProfile、教师档案TeacherProfile、账号表User。课程表要冗余存老师姓名选课表要冗余存课程名称和学生姓名。很多人觉得这样违反范式但在实际管理系统里这种冗余能大幅减少联表查询日常列表页展示和导出Excel时特别省事。Django的ORM虽然能做select_related但冗余字段配合信号回填在性能和开发效率上是很好的折中。具体模型设计参考# apps/courses/models.py from django.db import models from django.core.validators import MinValueValidator, MaxValueValidator class Course(models.Model): STATUS_CHOICES ( (0, 草稿), (1, 报名中), (2, 已截止), (3, 进行中), (4, 已结束), ) name models.CharField(课程名称, max_length50) teacher models.ForeignKey(users.TeacherProfile, verbose_name授课教师, on_deletemodels.CASCADE) teacher_name models.CharField(教师姓名, max_length20, editableFalse) week_day models.SmallIntegerField(星期, choices[ (1, 周一), (2, 周二), (3, 周三), (4, 周四), (5, 周五) ]) start_time models.TimeField(开始时间) end_time models.TimeField(结束时间) location models.CharField(上课地点, max_length100) capacity models.PositiveIntegerField(人数上限, default20) fee_per_session models.DecimalField(每课时费用, max_digits6, decimal_places2, default0) status models.SmallIntegerField(状态, choicesSTATUS_CHOICES, default0) created_at models.DateTimeField(创建时间, auto_now_addTrue) class Meta: verbose_name 课程 verbose_name_plural 课程 ordering [week_day, start_time]选课表Enrollment里需要做一个联合唯一约束保证同一个学生同一门课只能有一条记录同时课程和星期时间之间有“同一学生同时段冲突”的校验逻辑这个校验不能只靠数据库约束必须在视图层用事务锁处理后面第三部分具体展开。3. 核心模块实现与关键代码3.1 基于角色的认证体系扩展Django自带的User模型不必重写认证逻辑。我习惯用AbstractUser生成一个UserProfile加上role字段。角色用三个常量ADMIN、TEACHER、STUDENT分别对应1、2、3。管理员账号在创建时通过命令行或Admin后台创建老师和学生账号由管理员在后台录入并生成初始密码。实现自定义User模型时有一件非常关键的事必须在第一次数据库迁移之前设置AUTH_USER_MODEL。如果在迁移完成后再改数据库表结构混乱只能重置数据库重来。# config/settings.py AUTH_USER_MODEL users.User然后在users/models.py里from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): ROLE_CHOICES ( (1, 管理员), (2, 教师), (3, 学生), ) role models.SmallIntegerField(角色, choicesROLE_CHOICES, default3) phone models.CharField(手机号, max_length11, blankTrue)视图里判断角色可以写一个装饰器同时限制登录状态和角色权限。比如老师才能访问签到页面学生才能选课。我这里用Django自带login_required混合自定义装饰器防止未登录用户直接访问接口。from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied def teacher_required(view_func): login_required def wrapper(request, *args, **kwargs): if request.user.role ! 2: raise PermissionDenied return view_func(request, *args, **kwargs) return wrapper3.2 课程发布的表单处理管理员发布课程时页面要填课程名称、老师、时间、地点、人数上限等。Django的ModelForm可以减少很多手写校验但有一点要注意时间字段提交后要自定义校验结束时间必须晚于开始时间而且结束时间不能跨天。跨天对课后延时来说不合理因为放学后的延时服务一般不超过两小时所以直接拒掉。还有一个容易漏的问题老师的冲突检查。发布课程时要查同一老师在同一星期同一时间段是否已经有了别的课程。这个查询语句很直接用filter配合时间段重叠判断。时间重叠的定义是新课程开始时间小于已有课程结束时间并且新课程结束时间大于已有课程开始时间。def check_teacher_conflict(teacher_profile, week_day, start_time, end_time, exclude_idNone): qs Course.objects.filter(teacherteacher_profile, week_dayweek_day) if exclude_id: qs qs.exclude(idexclude_id) for course in qs: if start_time course.end_time and end_time course.start_time: return False return True这段逻辑也适用于后面学生选课的时间冲突检查只是把教师换成学生把课程换成选课记录。3.3 学生选课与避坑策略选课是整个系统最可能出现并发问题的环节。比如一门剩余名额只剩下1个两个学生同时点选课如果不加锁都可能通过人数校验最后超员。Django默认的事务隔离级别下两个请求并发读取到同样的剩余数量然后各自插入一条选课记录就比课程容量多出人了。解决方案有两个层次。第一层是事务加行锁对于选课操作先锁定课程记录再检查人数并插入选课记录。Django里用select_for_update可以锁定行直到事务结束配合transaction.atomic保证原子性。from django.db import transaction def enroll_course(request, course_id): user request.user course Course.objects.select_for_update().get( idcourse_id, status1 ) # 锁定课程行 existed Enrollment.objects.filter( studentuser.student_profile, coursecourse ).exists() if existed: return JsonResponse({code: 1, msg: 不能重复报名}) current_count Enrollment.objects.filter(coursecourse, status__in(0, 2)).count() if current_count course.capacity: return JsonResponse({code: 1, msg: 名额已满}) same_time_conflict check_student_time_conflict(user.student_profile, course.week_day, course.start_time, course.end_time) if same_time_conflict: return JsonResponse({code: 1, msg: 该时间段已有课程}) Enrollment.objects.create( studentuser.student_profile, coursecourse, status0 ) return JsonResponse({code: 0, msg: 选课成功})第二层是数据库层加唯一约束在Enrollment表上联合唯一student_id和course_id防止重复插入。这样即使代码漏判数据库也会抛IntegrityError至少不会出现同一学生同一课程两条记录。我建议两层都做。并发压测的时候只用select_for_update其实已经够用但唯一约束在后续手工插数清理时能兜底不会出现脏数据。补充一下学生时间冲突的具体SQL写法。可以用Q对象构造一个复杂查询from django.db.models import Q def check_student_time_conflict(student, week_day, start_time, end_time): conflicting_courses Course.objects.filter( week_dayweek_day, enrollment__studentstudent, enrollment__status__in(0, 2) ).filter( Q(start_time__ltend_time) Q(end_time__gtstart_time) ) return conflicting_courses.exists()这里把Course和Enrollment做反向关联查询一个学生所有有效选课中是否有时间重叠的课程。status过滤掉已取消的记录避免把已经取消的课程也算成冲突。3.4 考勤签到与补课逻辑考勤模块给老师用一个选课记录对应一次课的出勤状态。但一门课会有很多次课所以单靠选课记录存状态不够。我单独建一个Attendance表字段包括选课记录、上课日期、状态签到、缺勤、请假、补课。老师端打开课程列表系统生成当次课的所有学生考勤记录没有的才允许签到。生成当次课记录的逻辑很简单按日期找到当前Course的所有有效选课然后批量创建Attendance状态默认缺勤老师再手动把到课学生改成签到。当然这里是老师手动点名不做人脸识别那些毕竟延时服务签到不需要太重的硬件。考勤记录生成时注意去重同一天同一课程不能生成两次。可以用unique_together约束course和class_date但这里的course并不是直接外键Course而是Attendance里的course。如果一门课一天内可能有多个班次就需要再引入一个“课次”概念。为了简化我们要求每周每门课只有一个固定时间段所以课程表已经确定了week_day和start_time一天最多一次课直接唯一约束就可以。3.5 费用统计报表实现费用统计分成学生账单和教师结算两块。学生费用等于“学生所选课程每课时费用乘实际上课次数”教师结算等于“授课课程每课时费用乘实际上课次数”。这里的实际上课次数从Attendance表里统计状态为“签到”才计入。请假和缺勤不计费补课的状态单独算如果补课完成了也算计费次数。一个简单的统计视图# apps/stats/views.py from django.db.models import Sum, Count def student_bill(request, student_id): records (Enrollment.objects .filter(student_idstudent_id) .values(course__name, course__fee_per_session) .annotate( session_countCount(attendance, filtermodels.Q( attendance__status2)), # 2为签到 total_feemodels.F(course__fee_per_session) * models.Count(attendance, filtermodels.Q( attendance__status2)) )) return render(request, stats/bill.html, {records: records})统计语句用annotate配合filter可以一次查询搞定比先取选课列表再循环查考勤高效得多。费用计算这里有个细节每课时费用建议直接用DecimalField不要用FloatField。FloatField算钱会有精度误差尽管校内统计可能并不涉及银行级精度但豆腐渣工程容易在合计时出现248.999999这样的问题。DecimalField配合decimal模块处理输出到模板时再格式化能避免这种尴尬。4. 常见问题与排查技巧实录4.1 并发选课超员名额变负数我在联调阶段用两个浏览器窗口同时模拟两个学生选同一门只剩一个名额的课程结果两条记录都插入成功课程已选人数变成2超出容量。排查思路是重启使用select_for_update后问题消失但当时还需要确认锁是否真的生效。注意select_for_update必须放在事务里执行单独调用不会报错但锁在事务提交后立即释放如果视图函数没有加transaction.atomic整个查询和插入都不在同一个事务里行锁形同虚设。经验教训用select_for_update的视图函数必须有transaction.atomic装饰或者在自定义上下文里用with transaction.atomic()包裹整段业务代码。Django官方文档里也强调过只有InnoDB等支持行级锁的数据库才生效SQLite默认不支持select_for_update会直接报NotSupportedError。开发环境用SQLite方便但测试并发场景必须切换到MySQL或PostgreSQL。4.2 时间冲突漏判学生一天被排进两门课另一个坑是时间冲突检查没有排除“已取消”的选课状态。学生先选了一门课后来取消了但系统中还有一条status2已取消的记录。如果冲突查询不按状态过滤学生就会被自己取消过的课程挡住再选新课程时一直提示时间冲突。我排查时发现处理逻辑遗漏了状态过滤导致一个学生同一个时间段有两门“有效”课程记录而其中一门明明已经取消。修正方式就是在时间冲突查询里把所有已取消、已退课的状态都排除掉。建议给选课状态做一个枚举封装不要裸用0、1、2代码里加中文注释后续维护的人能一眼看懂状态含义。4.3 时区引发的考勤日期错乱有一段时间考勤日期总是比实际日期早8小时原因出在Django settings里的TIME_ZONE设成了UTC。学生晚上6点选课记录创建时间变成了第二天凌晨2点UTC8时差导致统计日期归属错位。解决办法是把settings里TIME_ZONE改为Asia/Shanghai同时USE_TZ保持为True然后用本地时间做日期运算时注意先activate时区。具体来说# config/settings.py TIME_ZONE Asia/Shanghai USE_TZ True在统计视图里统一用django.utils.timezone.localdate()获取当天日期不要直接用datetime.datetime.now()因为后者在USE_TZTrue时仍是UTC时间。这是很典型的一个坑尤其当系统部署在海外服务器或容器里时手动设置TZ最容易忽视。4.4 数据库迁移冲突字段加了又删掉多人协作或自己反复改模型时容易产生迁移文件冲突。比如在Course里加了remark字段迁移后又删了但删除的迁移文件和之前的0001、0002混在一起可能会提示“django.db.migrations.exceptions.InconsistentMigrationHistory”。这时不要手工删数据库的django_migrations表那是最粗暴也最糟糕的办法。推荐的做法是如果开发阶段数据不重要直接执行python manage.py makemigrations courses --empty生成一个空迁移然后在这个空迁移里用RunPython清理历史或者把之前的一串迁移压缩为初始迁移。如果已经上线且有数据则需要按依赖顺序把迁移回放一遍同时写数据脚本迁移业务数据。我处理模拟项目X时是开发阶段直接删掉所有迁移文件并重建数据库省时省力。4.5 静态文件404样式全丢部署到测试服务器后后台和前台页面样式全部丢失。典型原因是Django项目settings里没有配置STATIC_ROOT也没有执行collectstatic。本地开发时Django会自己处理静态文件但线上必须把分散在app里的static目录统一收集到一个目录然后由Nginx或中间件去服务。排查步骤很简单先执行python manage.py collectstatic --noinput检查是否生成了STATIC_ROOT目录再看Nginx配置里location /static/是否指向这个目录最后确认settings里STATIC_URL和STATIC_ROOT路径一致。这一套走下来样式问题基本都能解决。5. 部署上线要点与性能优化5.1 开发环境与生产环境配置分离很多人习惯在同一个settings.py里改配置要么用环境变量要么直接硬编码。我建议至少拆成settings_base.py、settings_dev.py、settings_prod.py三个文件通过环境变量DJANGO_SETTINGS_MODULE切换。开发环境开启DEBUGTrue使用SQLite生产环境DEBUGFalse使用MySQL或PostgreSQL。生产环境安全上要注意几个点SECRET_KEY不能提交到代码仓库建议用环境变量读取DEBUG必须设为False否则报错页面会泄露路径和配置信息ALLOWED_HOSTS要配置成实际的域名或IP否则会收到DisallowedHost异常。5.2 使用Gunicorn和Nginx部署Django自带的runserver只适合开发环境生产上我会用Gunicorn作为WSGI服务器Nginx做反向代理并托管静态文件。简单来说Nginx接收用户浏览器的请求如果请求的是静态文件直接返回文件内容如果是动态请求就转发给Gunicorn进程处理Gunicorn再交给Django应用。Gunicorn启动命令可以参考gunicorn config.wsgi:application --bind 127.0.0.1:8000 --workers 3 --timeout 60workers数量一般建议是CPU核心数的2倍加1但校内系统并发不高3到4个worker完全够用。Nginx配置里做动静分离server { listen 80; server_name your-domain.com; location /static/ { alias /var/www/after_school/static/; } location /media/ { alias /var/www/after_school/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5.3 数据库连接与查询性能优化课后延时系统的数据量级不大不需要复杂的缓存架构但有几个查询细节值得注意。列表页展示课程时模板里如果访问course.teacher.name而之前没有用select_related就会产生N1查询。解决办法是在视图里这样写course_list Course.objects.select_related(teacher).all()select_related适合ForeignKey和OneToOneField用一条SQL把关联表join出来避免循环中重复查询。如果模型里面还有反向关联要用prefetch_related两者适用场景不同。我建议在开发初期就把Django Debug Toolbar装上直接看每次请求的SQL条数和耗时。如果某个页面SQL查询超过50条很大概率就有N1问题优先优化。5.4 备份与恢复教务数据不能丢课程表、考勤记录、账单都是重要数据。至少要每天备份MySQL数据库。Linux下可以用crontab定时任务mysqldump -u root -p after_school --single-transaction | gzip /backup/after_school_$(date %F).sql.gz同时把media里的上传文件比如课程封面、老师头像单独备份一份。恢复时gunzip after_school_2025-03-14.sql.gz | mysql -u root -p after_school恢复之前确认数据库名、密码、字符集一致不然中文数据容易变成乱码。6. 项目迭代与扩展方向6.1 从单校区到多校区管理如果学校有多个校区原有模型需要增加一个Campus模型课程、教师、学生都要加上campus外键。选课逻辑里所有查询都要带上校区过滤否则A校区的学生会看到B校区的课程。这种扩展在Django模型里改动不算大但工作量比较繁琐不建议一开始就做好除非需求方明确说有多校区计划。6.2 增加消息通知能力家长最关心的就是孩子有没有正常到课、有没有请假。可以在考勤状态变更时发送站内信和短信通知。Django里可以用signals监听Attendance的post_save事件然后触发通知任务。如果不想引入Celery简单的做法是把这个通知任务发到后台线程或等请求响应用异步队列但注意别把耗时操作直接写在视图里造成卡顿。对小型系统来说直接用后台管理页面查看考勤已经够用通知可以做成选做功能。6.3 家长端小程序对接预留很多学校希望家长用手机查看课程和考勤而不是登录电脑网页。这里可以预先在API层预留数据接口用Django Rest Framework添加几个只读接口比如课程列表、学生选课、考勤记录。小程序端只需请求这些接口并做展示核心的选课和考勤逻辑仍放在Web端。接口设计时注意每个接口都要鉴权建议用token认证而不是session认证小程序里管理session比较麻烦。7. 项目开发过程复盘7.1 需求调研最容易遗漏的隐形规则我在项目开始时花了大半天和需求方梳理业务流程最后发现有几个隐形需求是需求方一开始没主动提的。比如一个老师能不能同时上两门课同一时间段一个学生最多能报几门请假后课程费用是否退还这些规则如果不说清楚代码实现路径会完全不同。我建议做这类管理系统前拿一张白纸列出所有角色和每种操作的前置条件一条条和需求方确认打钩。后期改需求代价比前期大得多。7.2 开发节奏安排功能开发顺序建议是用户模型与认证 - 课程管理 - 学生选课 - 考勤 - 统计报表 - 部署上线。每一步结束都跑一遍Django TestCase哪怕只写几个简单的模型测试和视图状态码测试也能大幅降低联调阶段的低级错误。选课模块是整个系统的核心这部分多做几次并发压测和边界测试比如人数满员后再选、同一时间重复选课确认不会有脏数据。7.3 代码书写习惯在项目里我坚持用Django的verbose_name给每个字段写中文名在Admin后台和管理页面上都能直接看到可读名称省得来回查模型定义。字段命名统一用snake_case主键不用自定义用Django默认的自增id。外键字段名写明指向对象如course和teacher不要叫c_id和t_id时间一长根本分不清。还有一个习惯是每写一个模型都顺手把__str__方法实现掉让后台列表显示的是可读文本而不是“Course object (12)”。8. 运维监控与数据校验8.1 日志记录与错误告警生产环境开了日志之后可以查看到底是什么请求在报错。Django的LOGGING配置里分文件记录错误和普通请求至少把error级别单独发到文件或者用邮件告警。我习惯把日志独立配置# config/settings_prod.py LOGGING { version: 1, disable_existing_loggers: False, handlers: { file: { level: ERROR, class: logging.FileHandler, filename: /var/log/after_school/error.log, }, }, loggers: { django: { handlers: [file], level: ERROR, propagate: True, }, }, }8.2 定时任务与系统维护课程状态自动流转可以使用Django的定时任务扩展比如django-crontab或Celery beat。每天晚上凌晨把处于“报名中”且截止日期已过的课程改成“已截止”。用crontab来执行管理命令最简单不用引入Celery那么重的组件。写一个management command比如close_courses.py然后crontab里加一行0 0 * * * cd /var/www/after_school /usr/bin/python3 manage.py close_courses这样系统可以自动结束过期课程不需要人工每天盯。8.3 数据完整性校验上线初期最好每周导出一份报名和考勤核对表人工抽查几个班级的数据是否和纸质记录一致。系统虽然已经做了一层唯一约束和事务锁但教务老师手动通过Admin编辑数据时仍可能绕过视图层的业务校验。可以在Admin的save_model方法里加入时间冲突校验或者定期写一个检查脚本扫描冲突数据。这类数据完整性检查脚本在初期很有必要能及时发现潜在的异常状态。我自己在模拟项目X上线后第二周就发现一批考勤记录有重复原因是一个老师同一天点了两次签到视图里没有做“当天同课程只能签到一次”的校验。后来加了unique_together并在视图里try except IntegrityError才算真正堵住漏洞。9. 最终体验总结这个基于Django的中小学课后延时服务系统整体开发下来最大的感受是业务边界比技术难点更值得花时间。Django本身已经把用户认证、ORM、Admin这些底座都准备好了真正的难点全在业务规则上——时间冲突怎么判、并发报名怎么锁、费用怎么算、状态怎么流转。把这些业务规则用事务、唯一约束、状态机理清楚系统就会非常稳定。在实际运行中选课模块的select_for_update加联合唯一约束这套方案足以应对一个普通学校几百个学生同时选课的并发量。课程发布到报名结束的闭环配合考勤和费用统计让教务老师从手动表格中解放出来数据也基本做到了自洽。如果后续家长端需要接入小程序或AppDjango Rest Framework扩展起来也很方便整体技术选型对于这个规模的管理系统是很合理的。最后分享一个小技巧在Django项目里把“业务规则”写进模型层而不是散落在视图各处。比如时间冲突判断、状态变更校验都封装成模型方法视图只是薄薄一层调用。这样项目越往后维护越能体会到好处。每次改动业务规则只需动一个地方不用满项目搜索过滤条件这也是我踩过不少坑之后的经验。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑