资讯动态

Django+Bootstrap社区疫情防控系统:从需求到部署全记录

发布时间:2026/10/13 4:37:51 来源:尧图企业网站定制
去年帮一个学弟远程调试他的毕业设计项目——基于Django和Bootstrap的社区疫情防控系统。他在本地跑得好好的一到服务器上就白屏折腾了三个小时才发现是静态文件没有收集。这种问题在毕业设计里太常见了功能写了不少但部署经验几乎为零。今天我会完整复盘这套系统的设计和实现过程从需求拆解、技术选型、核心模块开发到部署调试和常见坑点一次性讲清楚。无论你是正在选毕设题目的学生还是想通过一个完整Django项目入门Web开发的初学者这篇文章都可以当作一条参考路线。这个系统做得好的地方在于它没有堆砌功能而是非常务实地解决了一个具体问题社区在人员管理、健康信息收集、出入登记这几项日常事务上的信息混乱。居民通过网页完成每日打卡和出入申请社区管理员统一审核和统计系统管理员负责账号和数据维护。三个角色各司其职让一套普通的CRUD系统有了真实业务的味道。1. 需求拆解先把“社区疫情防控”这个题目变成一个具体的系统很多同学拿到这个题目后第一反应就是“我要做一个很全的系统”结果需求越写越多最后连核心流程都没有跑通。我当时给学弟的建议很简单不要急着列功能先想清楚这个系统每天服务的是谁、他们要干什么、哪些环节是必须由系统完成的哪些用了纸质登记也完全可以。1.1 场景里的三个核心角色和各自痛点社区场景里最常出现的三类人是普通居民、社区工作人员、上级或系统维护人员。居民最关心的是“我今天上报健康状态方不方便”“我出小区要申请流程麻不麻烦”。社区工作人员最关心的是“今天有多少人没上报”“从外地回来的人有没有按规定登记”“谁正在隔离期内”。系统管理员关心的则是“账号能不能管住”“数据能不能备份”。以此为出发点系统被拆成了几个明确的业务模块居民端注册登录、每日健康打卡、出入申请、查看公告、查看自己的历史记录社区管理员端居民档案管理、打卡数据审核与统计、出入审批、公告发布、异常状态标记系统管理员端用户管理、数据导出、系统设置这一点看起来是废话但绝大多数做得失败的毕设就是没有在开发前后把这三角色的话想清楚。角色清楚了数据库和页面结构自然就跟着清楚了。1.2 毕业设计的功能边界必须做减法的MVP我给学弟划了一条MVP最小可行产品线只要把“居民每日打卡”和“社区管理员查看未打卡人员名单”这两件事做成闭环系统就已经及格了。在此基础上再补上出入申请和公告管理就是完整度相当不错的毕设。优先实现的核心业务点用户注册和登录且普通用户不能访问管理页面每日健康打卡同一个人同一天只能提交一次管理员能按日期查看应报人数、已报人数、未报人数管理员能新增公告居民在首页能看到出入申请居民提交管理员审批状态可追踪至于什么大数据分析、消息推送这类花哨功能如果只是用现成库拼一个demo代码量也上去了却很难在答辩时讲出深度。不如先把基础轮子做扎实。1.3 扩展思考为什么这类毕业设计不容易“跑偏”Django项目最大的优点是高度结构化和自带许多规范。只要一开始把应用拆分合理后续无论是扩展功能还是排查问题都比较顺手。这里给个小建议把用户相关的功能全部放进一个app例如account把业务功能放进另一个app例如community两者解耦。后面你会感谢自己在项目开头做这个决定。2. 技术选型Django和Bootstrap这套组合到底赢在哪选择一个技术栈绝不能只看“会不会”要看它能否在有限时间内产出一个稳定、可演示、好讲解的系统。Django Bootstrap正是这个场景里最稳妥的组合之一。2.1 Django不是“杀鸡用牛刀”而是帮你少写一半底层代码用Django做这个系统收益最大的是四块东西ORM、Admin后台、认证体系和表单处理。ORM让你不用手写SQL数据表通过models.py定义后直接生成迁移文件改起来也好改。Admin后台带完整登录认证稍微魔改一下就能变成系统管理员的管理界面这个对毕设来说简直是外挂。自带的User模型和auth模块把注册、登录、session、权限控制全部做了封装比从零写登录安全得多。Django Form配合模板既能做表单渲染又能做数据校验还能防御CSRF攻击。当然也有人说Django过于“重”。但在毕业设计这种项目里“重”意味着标准答案多、教程多、出问题能搜到解决方案。相比用原生PHP或Node裸写Django明显更适合学生。2.2 Bootstrap负责解决“页面不太难看”这件事很多后端同学做前端最头疼的一件事就是CSS。Bootstrap的价值在于通过栅格系统和预设组件能让没有任何审美基础的人快速搭出能看的页面。这套系统里的页面我统一采用Bootstrap 4或5具体版本可以看当时环境选择。核心用法就三样栅格布局做响应式手机上也能操作卡片、表格、表单控件、徽章、模态框做业务展示工具类做按钮、间距、文字样式调整我们并不需要复杂的JavaScript交互。Django模板加一个表格、一个表单、一个图表库足够完成全部演示需求。2.3 数据库选型与核心模型设计开发环境我建议直接用SQLite零配置、单文件、跑起来舒服。部署到生产环境时再切换到MySQL或PostgreSQL通过Django的DATABASES配置切换业务代码基本不用动。以下是系统最核心的几张表的models.py结构按Django的标准方式定义from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): ROLE_CHOICES ( (resident, 居民), (staff, 社区管理员), (admin, 系统管理员), ) user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) role models.CharField(角色, max_length10, choicesROLE_CHOICES, defaultresident) community_name models.CharField(社区名称, max_length100, blankTrue) phone models.CharField(联系电话, max_length20, blankTrue) def __str__(self): return self.user.username class HealthCheck(models.Model): STATUS_CHOICES ( (normal, 正常), (abnormal, 异常), ) user models.ForeignKey(User, on_deletemodels.CASCADE, related_namehealth_checks) check_date models.DateField(填报日期, auto_now_addTrue) temperature models.DecimalField(体温, max_digits4, decimal_places1) status models.CharField(状态, max_length10, choicesSTATUS_CHOICES, defaultnormal) symptom models.TextField(症状描述, blankTrue) is_in_quarantine models.BooleanField(是否处于隔离观察期, defaultFalse) created_at models.DateTimeField(提交时间, auto_now_addTrue) class Meta: unique_together (user, check_date) ordering [-check_date] class AccessLog(models.Model): STATUS_CHOICES ( (pending, 待审核), (approved, 已通过), (rejected, 已拒绝), ) user models.ForeignKey(User, on_deletemodels.CASCADE, related_nameaccess_logs) reason models.CharField(出入事由, max_length200) expect_time models.DateTimeField(预计出入时间) status models.CharField(审核状态, max_length10, choicesSTATUS_CHOICES, defaultpending) applied_at models.DateTimeField(申请时间, auto_now_addTrue) reviewed_by models.ForeignKey(User, on_deletemodels.SET_NULL, nullTrue, blankTrue, related_namereviewed_logs) reviewed_at models.DateTimeField(审核时间, nullTrue, blankTrue) class Notice(models.Model): title models.CharField(标题, max_length200) content models.TextField(内容) published_at models.DateTimeField(发布时间, auto_now_addTrue) publisher models.ForeignKey(User, on_deletemodels.CASCADE, related_namenotices)这里的几个设计决策很关键HealthCheck表设置了unique_together (user, check_date)从数据库层面杜绝了同一天重复打卡的问题。UserProfile和User是一对一关联避免直接修改Django自带的User表后期升级版本不会踩迁移的坑。AccessLog中的reviewed_by使用on_deletemodels.SET_NULL是因为审核人可能被删掉但出入记录不能跟着丢。2.4 项目结构怎么搭创建项目后我会按如下结构整理project_name/ account/ # 注册、登录、用户角色 community/ # 打卡、出入申请、公告 static/ # Bootstrap、Chart.js、自定义css/js templates/ account/ # 登录和注册页面 community/ # 首页、打卡页、出入申请等 manage.py db.sqlite3static和templates放在项目根目录然后在settings.py中配置STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static] TEMPLATES [ { DIRS: [BASE_DIR / templates], ... } ]这个配置在本地调试没问题但部署时要把STATICFILES_DIRS和STATIC_ROOT区分开。后面的部署环节我会单独说明。3. 核心业务模块实现从需求到可运行代码的落地过程3.1 健康打卡模块一张表搞定业务闭环打卡模块是整个系统的脸面也是数据量最大的模块。居民登录后进入页面先看今天是否已经打过卡如果打过就直接展示当日记录不允许再次提交。视图实现思路如下from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .forms import HealthCheckForm from .models import HealthCheck login_required def daily_check(request): today request.user.health_checks.filter(check_datetimezone.localdate()).first() if request.method POST: form HealthCheckForm(request.POST) if form.is_valid(): record form.save(commitFalse) record.user request.user record.save() return redirect(check_success) else: form HealthCheckForm() return render(request, community/daily_check.html, {form: form, today: today})表单部分不用手动逐个渲染直接用forms.ModelFormfrom django import forms from .models import HealthCheck class HealthCheckForm(forms.ModelForm): class Meta: model HealthCheck fields [temperature, status, symptom, is_in_quarantine] widgets { temperature: forms.NumberInput(attrs{class: form-control}), status: forms.Select(attrs{class: form-control}), symptom: forms.Textarea(attrs{class: form-control, rows: 3}), is_in_quarantine: forms.CheckboxInput(attrs{class: form-check-input}), }这里做一个常见防重逻辑。模型层面已经用unique_together保护了但用户在页面上多次提交时会看到数据库冲突的错误体验很差。所以视图层还需要先做一次查询判断如果today已存在就返回提示并跳转到结果页。社区管理员的统计页面则依赖Django的聚合函数from django.db.models import Count def staff_dashboard(request): health_records HealthCheck.objects.filter(check_datetimezone.localdate()) total_users UserProfile.objects.filter(roleresident).count() submitted health_records.values(user).distinct().count() unsubmitted total_users - submitted abnormal health_records.filter(statusabnormal).count() ...这样做的好处是页面数据完全由后端提供模板里只需要展示变量配合一个简单的图表库就能生成清晰的仪表盘。3.2 出入审批模块用状态字段完成一个轻量业务流程出入申请本质上是一条带状态的记录。居民提交时状态为“待审核”管理员在管理页面对待审核记录进行通过或拒绝审核人字段记录操作者这样责任链清晰答辩时可以说是完成了“流程追踪”。视图实现不复杂关键是权限控制。这里不用Django自带的Group而是通过UserProfile的role字段判断角色自定义一个装饰器from functools import wraps from django.core.exceptions import PermissionDenied def role_required(*roles): def decorator(view_func): wraps(view_func) def wrapper(request, *args, **kwargs): profile request.user.profile if profile.role not in roles: raise PermissionDenied return view_func(request, *args, **kwargs) return wrapper return decorator然后在管理端视图上加装饰器login_required role_required(staff, admin) def review_access(request, pk): ...这套自定义权限逻辑比直接使用user.is_staff更灵活居民账号不会被误当成管理员同时也能在页面模板中判断显示的菜单项。3.3 公告模块与首页展示公告模块是最简单的增删改查但有一个细节值得注意公告列表最好做分页而且首页只显示前三条摘要。这样既保持首页清爽也展示了开发者的工程化思维。from django.core.paginator import Paginator def notice_list(request): notices Notice.objects.all().order_by(-published_at) paginator Paginator(notices, 10) page_number request.GET.get(page) page_obj paginator.get_page(page_number) return render(request, community/notice_list.html, {page_obj: page_obj})3.4 数据可视化给管理员一个直观的仪表盘仪表盘如果全部用表格展示数据说服力会差一些。我当时给学弟选了个最简单的方式在页面里引入Chart.js的CDN后端把统计结果传成JSON前端直接调用。例如统计近7天打卡人数后端构造数据from django.db.models.functions import TruncDate from django.db.models import Count def chart_data(request): records ( HealthCheck.objects .filter(check_date__gtetimezone.localdate() - timedelta(days6)) .annotate(dayTruncDate(created_at)) .values(day) .annotate(totalCount(id, distinctTrue)) .order_by(day) ) labels [item[day].strftime(%m-%d) for item in records] data [item[total] for item in records] return JsonResponse({labels: labels, data: data})页面里用一个fetch或者直接在模板中把JSON注入到JavaScript变量里就能画出柱状图。不必引入重型的图表框架。4. 前端页面搭建Bootstrap和Django模板的正确配合方式很多学弟学妹在写前端时喜欢把每个页面都从头写一遍这是最浪费时间的地方。Django模板继承机制配合Bootstrap正确的姿势是“一个基础模板走天下”。4.1 base.html模板设计与页面扩展我做了一个含有导航栏、内容区、页脚的base.html所有页面只需继承它并重写content块。核心代码大致如下!DOCTYPE html html langzh-hans head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title{% block title %}社区管理系统{% endblock %}/title link relstylesheet href{% static bootstrap/css/bootstrap.min.css %} /head body nav classnavbar navbar-expand-lg navbar-dark bg-primary a classnavbar-brand href{% url index %}社区防疫管理系统/a div classcollapse navbar-collapse ul classnavbar-nav ml-auto {% if user.is_authenticated %} span classnavbar-text mr-2{{ user.username }}/span a classnav-link href{% url logout %}退出/a {% else %} a classnav-link href{% url login %}登录/a {% endif %} /ul /div /nav main classcontainer mt-4 {% block content %}{% endblock %} /main script src{% static bootstrap/js/bootstrap.bundle.min.js %}/script /body /html这里要强调一下静态文件的路径不要写错。本地调试时Django会自己找STATICFILES_DIRS但部署后必须执行collectstatic把所有静态文件复制到STATIC_ROOT目录再由nginx提供访问否则就会出现开头说的白屏问题。4.2 表格页与表单页的通用写法管理端的列表页我统一用Bootstrap卡片包一张表格表格head使用thead-dark状态列用badge显示颜色。例如健康打卡列表标签{% if record.status normal %} span classbadge badge-success正常/span {% else %} span classbadge badge-danger异常/span {% endif %}表单页则统一用form-group包裹字段因为之前已经在后台定义widget的class所以模板里会正常渲染出Bootstrap样式。如果不想每次定义widget可以改用django-crispy-forms但对这个项目而言手动指定字段样式已经完全够用还能少装一个依赖。4.3 页面权限与交互细节页面上的内容要跟着用户角色变化。居民登录后导航栏显示“健康打卡”“出入申请”“我的记录”管理员登录后则看到“待办审批”“数据统计”“公告管理”。这个操作通过在模板中判断profile.role完成。{% if user.profile.role staff or user.profile.role admin %} a classdropdown-item href{% url staff_dashboard %}数据统计/a {% endif %}需要注意的是前端隐藏按钮只能改善体验不能作为安全策略。真正的权限控制必须在视图层完成否则用户直接输入URL就能绕过隐藏菜单。这也是毕业设计答辩时老师经常会提问的地方。4.4 响应用户操作的提示信息毕业设计演示时最容易冷场的一种情况用户点击提交按钮后页面刷新表格确实新增了一条数据但没有反馈观众和评委都一脸懵。所以一定要用Django的messages框架。视图里加一行from django.contrib import messages messages.success(request, 健康打卡成功)模板base.html中加显示{% if messages %} {% for message in messages %} div classalert alert-{{ message.tags }}{{ message }}/div {% endfor %} {% endif %}这个细节看起来微不足道但在演示环节能显著提升整个流程的友好感。5. 本地调试到服务器部署经验、坑点和解决路径项目写完后距离“可以演示”还差一大步就是部署。很多人的代码在本地没有任何问题一到服务器上就各种白屏、403、静态文件丢失、数据库迁移报错。下面把高频问题集中列一下。5.1 最常见三个坑时区、CSRF、静态文件第一是时区。Django默认的TIME_ZONE是UTC如果settings.py里不改打卡记录的本地日期就会经常差半天。我的做法是TIME_ZONE Asia/Shanghai USE_TZ True第二是CSRF。调试用户打卡或出入申请时突然403十有八九是模板里的表单没有加{% csrf_token %}。加了之后还报错就要检查中间件是否启用、session是否正常。还要注意本地调试如果关闭了CSRF部署时一定要开回来。第三是静态文件。部署到服务器上后nginx负责提供静态文件Django不会再像开发模式下那样动态服务静态资源。所以必须python manage.py collectstatic而且settings.py需要配置STATIC_ROOTSTATIC_ROOT BASE_DIR / staticfiles如果没有这一步页面样式会全部丢失看起来就像前端写坏了。5.2 云服务器部署的基本路径这里只说最朴素的方案一台云服务器装Python 3.10、MySQL或SQLite环境用pip安装依赖再用gunicorn和nginx跑Django应用。最简单的部署步骤把本地代码通过git传到服务器创建虚拟环境安装requirements.txt配置settings.py中的ALLOWED_HOSTS为服务器公网IP或域名执行python manage.py migrate执行python manage.py collectstatic用gunicorn启动应用配置nginx反向代理把80端口转发到gunicorn端口nginx配置里最关键的三行location /static/ { alias /项目路径/staticfiles/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }部署完成后记得打开服务器安全组端口。80端口或自定义端口一定要在云平台的安全组规则里放行。5.3 帮学弟远程调试时发现的高频错误远程调试这件事其实挺能锻炼排查问题能力的。我最常遇到的几种情况项目是在Windows上开发的上传到服务器后sqlite3的路径大小写不对本地用SQLite没问题切到MySQL时忘记安装mysqlclient两套环境Python版本不一致导致某些语法在服务器上解释不了上传时遗漏了static文件夹collectstatic后资源缺失忘记执行迁移打开页面后报某某表不存在每次调试的第一步永远是看日志systemctl日志、nginx日志、Django的error.log。如果Django应用本身报错打开Debug模式重新访问一次错误栈会直接显示到浏览器上排查效率能大幅提升。上线后务必把DEBUG改成False否则安全风险很大。5.4 怎么把调试经验转化到文档里答辩除了看系统跑起来还会看文档。我建议把调试过程中遇到的问题整理成“常见问题和解决方案”一节放进说明文档哪怕写几条也很有价值因为这能证明是你亲手开发调试的而不是贴了一份网上的模板。文档至少包含项目背景与需求分析系统架构与功能模块说明数据库设计带表结构说明截图核心流程说明用户打卡流程、出入审批流程部署与运行手册测试过程和测试用例6. 复盘与可扩展方向这个系统其实还能走得更远顺利通过答辩之后不代表项目就没有优化空间了。任何一个毕设项目只要我们认真对待都值得继续打磨。这里谈谈我对这套系统的复盘以及如果换作现在重新做我会在哪些地方投入更多。6.1 现有实现中最明显的短板最大的短板是“消息通知”。现有的公告只能让居民到网站上查看不能主动推送给用户。在真实的社区场景里管理员更希望系统能通过短信或微信通知未打卡人员和待审核信息。这个逻辑本身不难难的是要接入第三方推送服务在毕设场景里容易引入额外的不确定性所以当时没有做。第二个短板是审核流程过于线性。所有出入申请都只经过一个人审批实际场景里可能需要楼栋长初审、社区复审。如果要重构可以在AccessLog里增加processing_step字段变成一个简单的流程引擎。第三个短板是数据可视化只覆盖了打卡汇总没有对异常体温的时空数据进行展示。如果加入小区楼栋维度的Map视图系统会立刻显得高级很多。6.2 如果继续开发我会优先加这几个功能我会优先做操作日志。给新增、修改、审核等关键操作写记录形成审计链路这在毕设里是极好的完整性亮点。Django的signal机制配合一个LogEntry模型就能实现。其次我会把导入导出功能做完善。管理员能通过Excel模板批量导入居民信息也能把某一段时间的打卡明细导出成xlsx文件。这个功能在实用场景里非常刚需而且在答辩时操作效果直观。最后我会把Django自带的Admin后台二开成纯管理端界面。当前管理界面是业务用户自己开发的保留Admin后台作为系统管理员入口两者互不干扰会显得体系更完整。6.3 关于“从源码到自主开发”的一点个人体会带学弟调试这个项目的过程中我感触最深的一点是源码和文档只是基础真正有价值的是在调试过程中把整个流程亲手走通。哪个页面链接到哪个视图、哪张表最后会被哪段查询关联只有在出问题时查过一遍才会印象清晰。如果你打算基于类似的成品项目去修改或扩展我的建议是三个字别全信。先看README再把数据库导出来看表结构然后本地跑起来按主流程一步一步点过去遇到报错就去读日志。这样折腾一两天项目才是你自己的。答辩时老师如果问“这个字段为什么这么设计”你能用自己的话讲出理由才是真正拿到了这套源码的价值。当时帮学弟把项目跑上线后他感慨说“原来每天打卡背后的逻辑这么简单”。确实是这样Django加上Bootstrap看起来技术难度不高但把业务需求分析清楚、把流程设计得可靠、把部署跑通这些才是工程能力的体现。希望这篇文章能帮你的毕设少走一点弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑