ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Python+Django打造大学生请假管理系统:全流程实战与避坑指南

用Python+Django打造大学生请假管理系统:全流程实战与避坑指南 从接到这个需求到跑通全流程我大概用了两周的业余时间。作为一个在校园里被各种纸面请假流程折磨过的人这次直接用 Python Django 从头写了一个大学生请假管理系统算是把“学生申请-辅导员审批-销假归档”这条链路彻底数字化了。这个项目里踩过的坑、绕过的弯以及那些在官方文档里不会明说的细节我觉得很值得拿出来聊聊尤其是对那些刚接触 Django 项目实战的新手这篇文章应该能帮你少走不少弯路。先说清楚这个系统到底做什么。它不是一个简单的增删改查 Demo而是一个能真正跑起来的请假业务闭环学生登录后可以提交请假申请包含请假类型、起止时间、事由、附件辅导员或班主任登录后可以看到待审批列表同意或驳回请假到期后学生需要申请销假所有操作都有状态记录管理员后台能导出统计报表。整个系统基于 Django 自带的后台体系做了二次开发没有引入复杂的前后端分离架构模板渲染 ORM 搞定一切。这样一个项目最大的价值就是让你完整经历一次“需求分析 → 数据建模 → 业务逻辑 → 权限控制 → 部署上线”的 Django 全流程比单纯看教程的收获大得多。1. 请假业务的数据建模这是整个系统的地基数据建模是这种管理系统的灵魂。我见过的很多新手项目一上来就写 models.py结果写到一半发现字段不够用、关系理不清最后全部推翻重来。请假这个业务看似简单但真正建模的时候有几个关键点需要想清楚。1.1 用户模型不要碰 User 表直接扩展 ProfileDjango 自带的auth.User提供了用户名、密码、邮箱、权限组这些基础设施够用但远不够支撑请假系统的业务需求。我们需要区分学生和教师两类角色而这两类角色在 Django 里都可以用User表承载只是通过一个类型字段区分。我的做法是创建一个UserProfile模型用OneToOneField关联到User然后补充role、student_id、department、phone这些业务字段。这么设计有一个明显的好处不动 Django 原生的认证逻辑所有依赖authenticate()、login()的 Django 内置功能都不受影响同时业务扩展性也打开了。# models.py from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): ROLE_CHOICES ( (student, 学生), (teacher, 教师), (admin, 管理员), ) user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) role models.CharField(max_length10, choicesROLE_CHOICES, defaultstudent) student_id models.CharField(max_length20, blankTrue, nullTrue, verbose_name学号) department models.CharField(max_length50, blankTrue, verbose_name院系) phone models.CharField(max_length20, blankTrue, verbose_name联系电话) def __str__(self): return f{self.user.username}-{self.get_role_display()}这里有个小细节related_nameprofile真的很重要。设了这个之后你在任何地方都能直接用request.user.profile.role拿到用户角色而不需要UserProfile.objects.get(userrequest.user)多查一次数据库。写代码的时候省事跑起来也少一次查询。1.2 LeaveApplication 模型状态机的核心载体请假单是系统的核心实体。设计它的字段时除了基本的学生信息、请假事由、开始结束时间最关键的其实是状态字段。请假是有生命周期状态的从“待审批”到“已批准 / 已驳回”再到“已销假”这个过程其实是一台状态机。class LeaveApplication(models.Model): STATUS_CHOICES ( (pending, 待审批), (approved, 已通过), (rejected, 已驳回), (cancelled, 已取消), (completed, 已销假), ) student models.ForeignKey(User, on_deletemodels.CASCADE, related_nameleave_applications) leave_type models.CharField(max_length20, choices( (sick, 病假), (personal, 事假), (emergency, 紧急情况), (other, 其他), ), verbose_name请假类型) start_time models.DateTimeField(verbose_name开始时间) end_time models.DateTimeField(verbose_name结束时间) reason models.TextField(verbose_name请假事由) attachment models.FileField(upload_toattachments/, blankTrue, nullTrue, verbose_name附件) status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpending, verbose_name状态) created_at models.DateTimeField(auto_now_addTrue, verbose_name提交时间) reviewed_by models.ForeignKey(User, on_deletemodels.SET_NULL, nullTrue, blankTrue, related_namereviewed_leaves, verbose_name审批人) review_comment models.TextField(blankTrue, verbose_name审批意见) reviewed_at models.DateTimeField(nullTrue, blankTrue, verbose_name审批时间) def __str__(self): return f{self.student.username}的请假单-{self.get_leave_type_display()}为什么把状态用字符串而不是数字新手很容易用 0、1、2 这种数字但过两个月回来看代码完全想不起 1 代表什么。用语义化字符串配合choicesDjango 的 admin 后台自动渲染成下拉框阅读性也好维护成本低这种选择在小型系统里是最划算的。ForeignKey(User, on_deletemodels.SET_NULL)这个细节也值得说。审批人字段如果审批老师离职了直接把请假单删掉肯定不对但应该保留历史记录。SET_NULL保证老师账号被删的时候请假单还在只是审批人指向空。这是数据完整性设计里常见的取舍。1.3 为什么不用状态字段直接等于 approved 判断权限很多人会写if leave.status approved这种代码但真实项目里状态多了以后这种散落的判断非常难维护。更好的做法是把状态判断收敛到模型方法里让业务逻辑内聚到模型层。def is_pending(self): return self.status pending def is_approved(self): return self.status approved这样的好处是如果以后状态增加了比如又加了“已转交辅导员”你只需要改 model 层所有模板里的判断逻辑不用动。这就是面向对象里“封装变化”的思路很多 Django 新手在视图中写了一堆if后来改版本时欲哭无泪就是这个原因。2. 表单设计与校验真正可用的系统靠的是细节数据模型定了接下来就是表单层。Django 的ModelForm能省不少事但要写出真正好用的请假表单还需要做不少定制工作。2.1 时间冲突校验数据库层面卡死UI 层面卡活请假系统中有一个必然出现的需求学生不能在同一时间提交两个重叠的请假单。这个校验有两个层级可以写第一层是表单层的校验给用户即时反馈第二层是视图逻辑里的再次校验防止有人绕过表单直接调接口。# forms.py from django import forms from .models import LeaveApplication class LeaveForm(forms.ModelForm): class Meta: model LeaveApplication fields [leave_type, start_time, end_time, reason, attachment] widgets { start_time: forms.DateTimeInput(attrs{type: datetime-local}), end_time: forms.DateTimeInput(attrs{type: datetime-local}), reason: forms.Textarea(attrs{rows: 5, placeholder: 请详细填写请假事由}), } def clean(self): cleaned_data super().clean() start_time cleaned_data.get(start_time) end_time cleaned_data.get(end_time) if start_time and end_time and start_time end_time: raise forms.ValidationError(结束时间必须在开始时间之后) return cleaned_data认真看一下这个widgets配置。HTML 的datetime-local输入框在 PC 浏览器上会渲染成原生日期时间选择器体验非常好用户不需要手输时间格式Django 后端拿到后也能正确解析成datetime对象。这是我在实际开发中特别推荐的一种组合比用第三方日期插件省心多了。2.2 文件上传限制别让任何人上传超出预期的附件附件上传功能看着简单但如果不做限制很容易踩坑。Django 的FileField默认是允许任意类型的实际应用里我们通常只需要 PDF、Word、图片这几种。我在视图中加了额外的校验用文件扩展名做白名单过滤。import os from django.core.exceptions import ValidationError def validate_attachment(value): ext os.path.splitext(value.name)[1].lower() allowed_exts [.pdf, .jpg, .jpeg, .png, .doc, .docx] if ext not in allowed_exts: raise ValidationError(f不支持的文件类型{ext}请上传 PDF、Word 或图片文件) if value.size 5 * 1024 * 1024: raise ValidationError(文件大小不能超过 5MB)关于这个文件大小限制我一开始没设置结果有人传了张 20MB 的手机照片页面直接卡了几秒钟才提交成功。后来才加上value.size检查。这里要说明value.size读的是上传文件的字节数跟磁盘上的文件大小是同步的配合前端maxlength整体体验会好很多。3. 视图与权限控制Django 自带认证体系的深入使用请假系统最核心的权限模型是三段式学生只能看自己的数据和提交申请老师只能审批自己权限范围内的申请管理员不参与业务但能看全量数据。这套权限控制借助 Django 的login_required装饰器和自定义user_passes_test就能干净利落地实现。3.1 用user_passes_test代替散落的 if 判断很多新手在视图里写一堆if request.user.profile.role ! teacher: return HttpResponseForbidden()这种写法虽然能用但代码可读性差而且每个视图都要复制一遍。更优雅的做法是写一个组合装饰器。from django.contrib.auth.decorators import login_required, user_passes_test def teacher_required(view_func): decorated_view login_required(user_passes_test( lambda u: u.is_authenticated and hasattr(u, profile) and u.profile.role teacher, login_url/accounts/login/ )(view_func)) return decorated_view使用的时候直接在视图函数上叠一层teacher_required def review_leave(request, leave_id): leave get_object_or_404(LeaveApplication, pkleave_id) # 业务逻辑...这个装饰器会把未登录用户踢到登录页已登录但角色不符的用户直接跳回登录页或返回 403逻辑全收敛到一个地方。你可能会问为什么不直接用 Django 自带的permission_required因为那是基于Permission模型的粒度比较粗请假系统这种基于自定义角色的校验用user_passes_test更贴合业务。3.2 学生只能看自己的请假单QuerySet 过滤器是最后一道防线权限控制不只是在视图层防君子更要在数据层防止越权访问。比如学生 A 直接手输 URL 去访问学生 B 的请假单详情如果代码里只写了LeaveApplication.objects.get(pkleave_id)那就能看到别人的隐私数据。这是非常典型的安全漏洞。正确的写法是在查询时强制加上用户过滤条件from django.shortcuts import get_object_or_404 def student_leave_detail(request, leave_id): leave get_object_or_404( LeaveApplication, pkleave_id, studentrequest.user # 关键强制绑定到当前用户 ) return render(request, leave/detail.html, {leave: leave})这一行studentrequest.user就是把数据库查询限定在当前登录用户自己的记录上。即使用户猜到了别人的请假单 ID也查不到数据。这种“查询时兜底”的思路在 Django 项目里属于最基本也是最重要的安全习惯。3.3 审批视图的业务逻辑细节审批功能的实现重点在于状态流转的严谨性。学生提交的请假单处于pending状态老师审批时只能把pending变成approved或rejected不能被重复审批。这个逻辑用一句“先检查状态再更新状态”描述很简单但代码里如果不加锁在高并发场景下可能出问题。def approve_leave(request, leave_id): if request.method POST: leave get_object_or_404( LeaveApplication, pkleave_id, statuspending # 关键只有待审批状态才能审批 ) leave.status approved leave.reviewed_by request.user leave.reviewed_at timezone.now() leave.review_comment request.POST.get(comment, ) leave.save() return redirect(leave:review_list)这里get_object_or_404里带了statuspending如果这个请假单已经被别人审批过了get_object_or_404会直接抛出 404从根上杜绝了同一条记录被反复审批的问题。这种方式比先get再判断if leave.status ! pending更简洁也少了竞态条件。4. 模板渲染与列表展示把数据变成可操作的前端页面Django 的模板系统虽然不是最时尚的但胜在原生、效率高。请假系统里的页面不多但每一页都要考虑人机交互的合理性。4.1 学生端一个表单 一张列表 一个详情页学生端最重要的页面是请假申请页。这个页面上方是表单下方是自己提交过的所有请假单列表。列表里的每一行需要显示请假类型、起止时间、状态用不同颜色标签展示、当前审批人。如果列表里直接放入“查看详情”和“取消申请”两个操作按钮整个页面的操作效率会高很多。!-- 学生列表页片段 -- table classtable table-hover thead tr th请假类型/th th开始时间/th th结束时间/th th状态/th th状态标签/th th操作/th /tr /thead tbody {% for leave in leaves %} tr td{{ leave.get_leave_type_display }}/td td{{ leave.start_time|date:Y-m-d H:i }}/td td{{ leave.end_time|date:Y-m-d H:i }}/td td{{ leave.get_status_display }}/td td {% if leave.status pending %} span classbadge bg-warning待审批/span {% elif leave.status approved %} span classbadge bg-success已通过/span {% elif leave.status rejected %} span classbadge bg-danger已驳回/span {% elif leave.status completed %} span classbadge bg-info已销假/span {% endif %} /td td a href{% url leave:detail leave.id %} classbtn btn-sm btn-outline-primary详情/a {% if leave.status pending %} a href{% url leave:cancel leave.id %} classbtn btn-sm btn-outline-danger取消/a {% endif %} /td /tr {% empty %} trtd colspan6 classtext-center text-muted暂无请假记录/td/tr {% endfor %} /tbody /table这个模板用了 Django 模板语法里的get_leave_type_display—— 这是choices字段自带的显示方法能直接把存储的sick变成“病假”非常方便。我在实际项目里见过有人为了显示中文类型专门写一个过滤器其实完全没必要get_xxx_display就是干这个的。4.2 教师端待审批列表的高效设计教师端页面有两个核心需求第一只看待审批的清单别把历史数据摊在面前第二审批动作要尽量少跳转。我的处理是做一个合并视图教师端一个页面展示所有待审批的请假单每条记录的详情用 Bootstrap 折叠面板>!-- 教师端审批列表片段 -- div classaccordion idapprovalAccordion {% for leave in pending_leaves %} div classaccordion-item h2 classaccordion-header button classaccordion-button typebutton>def status_badge_class(self): return { pending: bg-warning, approved: bg-success, rejected: bg-danger, cancelled: bg-secondary, completed: bg-info, }.get(self.status, bg-secondary)这样所有模板里只需要写{{ leave.status_badge_class }}就能统一渲染样式。这个思路其实就是把“展示逻辑”下沉到模型层模板更干净换主题更简单。5. 后台管理Django Admin 的定制化改造Django Admin 是这个框架送给开发者最好的礼物请假系统天然适合它来管理。默认的 admin 页面虽然能用但为了匹配业务场景有几个地方值得配置一下。5.1 自定义管理字段列表在admin.py里把list_display配成业务字段加上搜索和筛选器这样管理员在后台就能快速定位到某个人、某种状态的所有请假单。from django.contrib import admin from .models import LeaveApplication, UserProfile admin.register(LeaveApplication) class LeaveApplicationAdmin(admin.ModelAdmin): list_display (student, leave_type, start_time, end_time, status, reviewed_by, created_at) list_filter (status, leave_type, created_at) search_fields (student__username, reason) date_hierarchy created_at readonly_fields (created_at,) actions [mark_as_approved, mark_as_rejected] def mark_as_approved(self, request, queryset): queryset.update(statusapproved) mark_as_approved.short_description 批量标记为已通过 def mark_as_rejected(self, request, queryset): queryset.update(statusrejected) mark_as_rejected.short_description 批量标记为已驳回date_hierarchy这个配置很多人没用过它会在后台列表页生成一个年份/月份/日期的层级筛选条数据量大了以后非常管用。actions批量操作也是 admin 的亮点功能管理员在后台勾选多条记录一键批量通过或驳回这个体验比一条条点进去改快得多。值得一提的还有search_fields (student__username, reason)。这里用到了跨表搜索的语法在student关联的User表里搜username字段同时在reason字段里搜关键词全部由 Django ORM 在后台自动处理。这个字段设计对提升后端的检索效率很有帮助。5.2 用get_queryset限制非超级用户的可视范围如果你的辅导员也是用 admin 后台来管理本班学生的请假单那你需要在get_queryset()里控制数据范围。比如一个普通教师登录 admin只能看到自己审批过的请假单不能看到所有人的。这个需求如果用get_queryset实现非常简洁def get_queryset(self, request): qs super().get_queryset(request) if request.user.is_superuser: return qs if hasattr(request.user, profile) and request.user.profile.role teacher: return qs.filter(student__profile__department__containsrequest.user.profile.department) return qs.none()这个写法值得琢磨。非超级用户教师只能查看自己院系的请假单而学生用户干脆返回空集合。原生的 Django admin 在数据行级权限上不算强但配合get_queryset重写足以满足请假系统 90% 的需求。对于这个体量的系统为行级权限去引入 django-guardian 之类的第三方库有点大材小用了。6. 状态流转与销假逻辑让业务闭环完整请假系统如果只走到审批通过流程是没有闭环的。一个请假单应该走得完“申请 → 审批 → 销假”的全流程才是一个真正可用的系统。6.1 销假功能触发条件销假逻辑有两种实现方式第一种是学生手动点击“销假”按钮第二种是系统定时任务自动销假。我的方案是两种都保留如果请假结束时间过了 24 小时系统就把状态自动转为“已销假”学生也可以提前手动销假比如病假还没结束但已经恢复了。手动销假的视图很简单def complete_leave(request, leave_id): leave get_object_or_404( LeaveApplication, pkleave_id, studentrequest.user, statusapproved ) leave.status completed leave.save() messages.success(request, 销假成功祝您身体健康/一切顺利) return redirect(leave:student_list)这里的查询条件statusapproved很关键——只有已通过的请假单才能销假pending和rejected状态根本不存在销假的入口。自动销假的功能我用 Django 的management command加定时任务实现。写一个management/commands/auto_complete_leaves.pyfrom django.core.management.base import BaseCommand from django.utils import timezone from datetime import timedelta from leave.models import LeaveApplication class Command(BaseCommand): help 自动销假将已通过且结束时间超过24小时的请假单置为已完成 def handle(self, *args, **options): threshold timezone.now() - timedelta(hours24) expired_leaves LeaveApplication.objects.filter( statusapproved, end_time__ltthreshold ) count expired_leaves.update(statuscompleted) self.stdout.write(self.style.SUCCESS(f成功自动销假 {count} 条记录))然后在服务器上用 crontab 每天跑一次0 2 * * * cd /path/to/project /usr/bin/python3 manage.py auto_complete_leaves /var/log/leave_auto.log 21如果你用的是 Windows 环境那可以用计划任务的 schtasks 命令来配置同样的定时逻辑。这里我建议定时任务放在凌晨跑因为用户访问量低而且第二天一早所有人看到的销假状态都是最新的。6.2 状态机的边界情况处理在处理状态流转时我遇到过几个边界情况值得大家注意学生申请了 3 天假期第 2 天需要提前回校手动销假后剩余假期作废——这个逻辑天然成立不需要额外处理学生申请假期但老师 48 小时没审批这个时候系统要不要自动驳回我在项目里留了口子没有默认自动驳回而是在管理端加一个“超时未审批提醒”的功能每天定时给相关老师发一个邮件摘要提醒还有哪些请假单未处理请假时间跨月、跨学期怎么办这个要结合学校的校历功能但这套系统里我们做了简单限制超过 14 天的请假单学生提交时提示“请线下联系辅导员”审批时必须写备注。状态机的本质是所有流转路径都是提前定义好的非法路径一律拒绝进入这样数据永远干净。7. 性能优化与安全加固正经系统不能只有功能功能齐了之后性能和安全的坑也要提前趟一遍。请假系统是小并发应用但依然要保证必要的安全和性能。7.1 QuerySet 性能优化select_related 解决外键查询 N1Django ORM 有个经典的 N1 问题在模板里遍历请假单列表每条请假单都要访问一次关联的student外键如果列表有 50 条就要查询 51 次数据库。这在本地开发环境看不出来一上线数据量上来就明显变慢。解决办法是在视图里用select_related把所有外键关联提前一次性查出来def student_leave_list(request): leaves LeaveApplication.objects.filter(studentrequest.user).select_related(reviewed_by).order_by(-created_at) return render(request, leave/student_list.html, {leaves: leaves})select_related会把student和reviewed_by两个外键关联的表通过JOIN一次性查出来之后在模板里访问leave.student.username不再触发额外查询。我建议只要模板里有leave.student.xxx这种外键访问就惯性地加上select_related。这是个投资极小、收益极大的优化习惯。7.2 CSRF 保护与登录安全Django 默认开启了 CSRF 中间件所以只要你在所有POST表单里都加了{% csrf_token %}就能防住跨站请求伪造攻击。这个功能是默认的不需要额外配置但千万别手贱把它关掉我在本地调试时觉得麻烦关过一次后来重构时差点忘了加回来。另外登录视图我用了 Django 自带的LoginView而不是自己写这样能直接复用它的验证码、限流等扩展能力from django.contrib.auth.views import LoginView class CustomLoginView(LoginView): template_name registration/login.html redirect_authenticated_user True def get_success_url(self): return reverse_lazy(dashboard)redirect_authenticated_user True这个参数很重要——已登录用户再访问登录页会被直接重定向到首页不会出现登录页残留问题。在登录视图里这个细节很多人会忽略但实际上体验提升非常明显。7.3 数据库事务与数据一致性审批操作的“状态修改”看起来是一个简单 update但如果后续还要插入一条审批日志、更新学生剩余请假天数那这三个操作必须是原子的要么都成功要么都失败。Django 用transaction.atomic()包裹即可from django.db import transaction def approve_leave(request, leave_id): # ...省略权限验证... with transaction.atomic(): leave.status approved leave.reviewed_by request.user leave.reviewed_at timezone.now() leave.save() ApprovalLog.objects.create(leaveleave, actionapprove, operatorrequest.user)我在项目里加了ApprovalLog模型专门记录每次审批动作。这样即使有人误操作驳回了一条请假单也能通过日志追责和复盘。transaction.atomic()保证日志和状态修改是同一事务不会出现状态改了但日志没写的中间状态。8. 项目部署与真实避坑记录到这里系统功能已经完整了但项目真正完成还差最后一步部署。这一章分享几个我在部署过程中遇到的坑这些坑在教程里很难找到但每个都很真实。8.1 静态文件收集DEBUG 关闭后样式全丢的问题开发模式DEBUGTrue时Django 会自动处理静态文件。一旦DEBUGFalseDjango 就不再代理静态文件了样式和 JS 全部 404。解决方法是收集所有静态文件放到一个目录然后用 Nginx 来托管。python manage.py collectstatic --noinput这一步会让所有 App 的static/目录下的文件被复制到STATIC_ROOT指定的目录然后在 Nginx 配置里加一段location /static/ { alias /var/www/leave_project/static/; }如果忘记配置这个你会看到页面纯文本渲染没有任何样式。这不是代码问题而是部署配置问题排查起来非常容易绕弯路。8.2 数据库迁移重置迁移文件的最佳时机开发过程中你大概率会反复修改models.py于是会执行多次makemigrations和migrate。如果改到一半不想要之前的迁移记录了千万别直接删migrations目录下的文件再执行migrate否则数据库会出现django_migrations表记录和实际迁移文件不一致的问题。正确做法是# 1. 先备份数据库数据 # 2. 删除数据库中的 django_migrations 记录 # 3. 删除 models 层相关的 migration 文件但保留 __init__.py # 4. 重新执行 python manage.py makemigrations python manage.py migrate --run-syncdb我在这上面吃过几次亏后面学聪明了开发初期模型不稳定就保持一个initial migration等模型稳定后再“瘦身”一次。正式上线的项目迁移文件不要随便删每份迁移文件都是数据库演进的历史记录。8.3 时区问题北京时间不准的坑请假系统对时间非常敏感。Django 默认时区是UTC如果你不配置学生下午 3 点提交的请假单数据库里存的是07:00 UTC显示到页面上也会差 8 小时。这个问题困扰了我很久排查到最后发现是settings.py里的TIME_ZONE配置LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_TZ TrueUSE_TZ True的意思是在 Django 内部使用 UTC 存储时间在渲染模板时自动转换成当前时区。TIME_ZONE Asia/Shanghai控制显示的时区。这两个配置要配合使用如果只改TIME_ZONE不改USE_TZ显示依然可能是 UTC。关于处理时间我更推荐的做法是数据库统一存 UTC这是USE_TZTrue的默认行为所有展示层都依赖 Django 自动时区转换这样数据跨地域也不乱。8.4 文件上传路径配置别把文件存在代码目录里FileField的upload_to指定的是相对于MEDIA_ROOT的路径。我一开始偷懒直接把上传的文件放到了项目目录下结果代码一部署到服务器所有历史附件全丢了。正确的做法是把媒体文件独立出来MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media/)然后在 Nginx 里配置location /media/ { alias /var/www/leave_project/media/; }这样媒体文件在独立的media/目录下代码更新部署时不会互相干扰。9. 扩展思路这套系统还能怎么升级如果你打算在这个项目上继续深入或者准备把它写成毕业设计下面几条扩展思路可能对你有用。它们不需要推翻现有代码而是在现有基础上做增量。9.1 导出 Excel 统计报表学校的管理员经常需要统计每月各院系的请假数据。Django 里导出 Excel 最简单的方式是用openpyxl。你可以写一个管理命令或视图动态生成包含[学生姓名, 学号, 类型, 起始时间, 结束时间, 状态]的报表存成.xlsx返回给用户下载。这一步对于答辩展示和实际交付都很有加分项。9.2 通知推送机制审批通过或驳回之后学生可能希望收到通知。这里有几个档次的方案最简单的用 Django 自带的邮件服务在审批成功视图里调用send_mail给学生的邮箱发一封通知进阶一点可以接入企业微信/钉钉的 webhook 机器人用 POST 请求推送到学生手机二维码上实现即时通知。如果考虑校园网内的高频刷新场景甚至可以用 WebSocket 做后端实时推送但那样需要引入 Django Channels复杂度会明显上升属于“系统规模化之后再做”的优化方向。9.3 多级审批与请假额度一些学校对请假有分级规则3 天以内辅导员审批3-7 天院系领导审批7 天以上教务处审批。这种多级审批链路可以在现有基础上加一个approval_level字段或者做一个ApprovalFlow模型来处理流程链。想得再细一点还可以给每类学生设置每学期的累计请假上限比如事假不超过 14 天病假不计上限这需要在提交表单时做额度校验。我自己在做这个项目时的感受是Django 的 MVC 体系和自带的后台管理确实非常适合做这类信息管理系统核心开发任务解决之后整个系统的代码量和维护成本都在可控范围内。真正花时间的是把业务约束想清楚——请假状态怎么流转、谁能看什么、什么时间节点触发什么动作想清楚这些比堆代码重要得多。最后再分享一个从实操中总结的小技巧开发这类系统时千万别只在本地浏览器里点点点。把项目部署到一台真实的 Linux 服务器上跑一遍用手机浏览器访问、用校园网登录、让学生真的提交一条请假单让辅导员审一审你才会发现自己代码里还有多少想当然的地方。部署到真实环境暴露出的问题往往比你看十遍代码发现的问题都多。这个项目对我来说就是这样一步步打磨出来的希望对正在做类似工作的人有所帮助。
返回列表