news 2026/10/18 21:05:38

基于Python+Vue的支教管理系统开发实战:Django与Flask双后端架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python+Vue的支教管理系统开发实战:Django与Flask双后端架构

1. 为什么是“Python + Vue”:支教系统背后的真实业务场景

做这套系统的起因其实挺朴素。之前帮一个公益助学组织做信息化改造,他们的日常管理还停留在微信群接龙和Excel表格:哪个学校需要什么科目的支教老师、哪批志愿者能来几周、每天上了哪些课、物资发放记录,全靠几个人工整理。一学期下来,光是汇总各支教点的课时统计就要折腾好几天,出错率还很高。

团队里熟悉后端的人大多是写Python的,前端则需要一个上手快、组件化的框架,于是就有了这套“Python + Vue”的组合。说直白一点,乡村支教系统的核心不是炫技,而是把一套很零散的线下流程搬到线上,让它跑得通、改得动,业务人员经过简单培训就能用起来。

1.1 乡村支教系统到底要管哪些事

我把业务拆成了五条主线,这也是大部分支教协作场景的共性需求:

  • 支教学校管理:学校的基础信息,比如地点、年级数、学生人数、住宿条件,更关键的是“本学期缺什么科目的老师”。这个字段经常被忽略,但学校提出需求时全靠它来判断是否匹配得上。
  • 志愿者管理:姓名、联系方式、可支教时间段、擅长科目、有没有支教经历。这里有个现实问题:志愿者的时间往往只有几个月的窗口期,系统需要支持按时间段检索。
  • 支教项目发布与报名:支教团队根据学校需求发起某个支教点、某个时间段、某类课程的项目,志愿者在线报名,支教队审核,最后形成实际支教排期。
  • 课时与教学内容记录:每天上完课之后录入上课时间、课程主题、参与学生数,这是后续统计支教覆盖率、志愿时长的基础数据。
  • 教学资源归档:课件、教案、课程视频这些材料的分类上传和在线预览,尤其视频类资源,到了后来就牵扯到流媒体播放的问题(后文会专门讲)。

1.2 业务方提需求时最容易漏掉的细节

这里必须提醒一句:做这类系统的第一版,功能不用贪大,但有两个地方一开始就得想清楚,否则后面返工成本极高。

第一个是数据权限范围。支教团的组织架构一般是“总队—分队—支教点”,总队要能看全部数据,分队只能看自己负责的学校,学校老师只能提交需求和查看本校记录。如果第一版不做权限边界的预留,后期再往模型里加层级会很痛苦。

第二个是后端字段要预留扩展。我刚接这个项目时就吃过亏:学校模型里只设计了学生总数字段,结果一个合作学校被拆成了小学部和初中部,分别需要申报不同科目,最后只能靠加一个school_type字段和两个外键关系去补救。如果你现在也要做类似系统,建议一开始就把“归属校区”“所属学期”这类维度设计成独立字段,避免写死在某个表里。

2. Django 与 Flask 的选型取舍:一套系统里两套框架怎么分工

标题里同时出现了 Django 和 Flask,很多朋友看到第一反应是奇怪的:为什么不二选一?其实放到具体项目里,这两个框架完全可以共存,而且分工明确。

2.1 Django 做主后端的原因

对于支教管理系统这种典型的业务管理型项目,我选 Django 的理由非常直白:它的自带能力能省掉一大半基建工作。

  • 自带 ORM 和数据迁移:学校表、志愿者表、项目表、课时表之间关系复杂,Django 的 ORM 让我不用手写 SQL,python manage.py makemigrations一条命令就能同步结构。这在项目初期字段经常微调的时候,价值非常明显。
  • 自带 Admin 后台:这套系统的用户里,支教队的运营人员不是程序员。Django 自带的后台稍做定制,就能让运营人员自己维护字典数据、审核志愿者报名,不需要我单独开发一套内部管理页面。开发周期至少省了两周。
  • 自带用户认证体系:User模型、Group、Permission直接复用,再加上djangorestframework-simplejwt发 token,权限体系基本不用从零做起。

对应到项目实操,我建了一个teaching的 app,然后按业务域拆了schools、volunteers、projects、courses四个模块。没有用大而全的单体模块,因为支教系统的业务边界相对清晰,拆开之后,后面接 Flask 的服务也好定位。

2.2 Flask 在项目里扮演的角色

既然 Django 已经覆盖了主业务,为什么我还要引入 Flask?答案是:系统里有些轻量级服务,用 Django 去实现反而“重”了。

我留了两个场景给 Flask:

  • 视频转码切片服务:支教系统里老师会上传课堂实录,原始视频文件大,线上直接播放很卡。我写了一个独立的 Flask 服务,接收上传通知后,用ffmpeg把视频转成 HLS 格式(也就是.m3u8文件加 ts 切片)。这个服务逻辑简单、流量独立,专门放在一台轻量机器上,和 Django 主服务解耦。这就是热词里“vue播放m3u8”出现的原因,前端播放器配合 hls.js 或者 video.js 就能流畅播放。
  • 数据导出与统计接口:学期结束时需要给公益组织出报表,统计各支教点课时数、志愿者服务时长。这部分逻辑我用 Flask 写成一个独立服务,通过定时任务汇总数据,按学校维度输出 JSON 或 Excel,避免在主库里跑复杂的聚合查询。

从架构上看,Django 管“业务数据的增删改查”,Flask 管“周边辅助能力”。两套框架通过 HTTP 接口通信,谁也不影响谁。你要在简历或者项目答辩里说清楚这点,是很加分的:你不是简单“用 Django 做了一个系统”,而是做了基于多应用的职责拆分。

2.3 为什么 Pycharm 能同时管好这两套框架

这个项目我全程用 Pycharm 开发,原因不只是编辑器顺手。Pycharm 在这类多框架项目里,有几个功能确实省事:

  • 虚拟环境管理不用切窗口:我习惯在项目根目录建venv,Pycharm 会在打开项目时自动激活,装依赖直接用界面里的 Python Packages 管理面板。热词里有人搜“pycharm怎么安装pandas包”,实际操作就是在设置里找到 Project Interpreter,搜索 pandas 点击安装即可,比命令行直观不少。
  • 数据库工具:Professional 版自带的 Database 面板可以直接连 MySQL 或 SQLite,查看表结构、执行 SQL、甚至逆向生成模型定义。排查数据问题时我很少再单独打开数据库客户端。
  • HTTP Client:平时调试接口不需要额外开 Postman,直接在.http文件里写请求,比如模拟志愿者登录拿 token,再带上 token 调报名接口,非常方便。下面我会专门演示这个用法。

3. 后端核心设计:从建表到接口的完整实现

这一章把后端关键代码和设计思路展开,你可以直接参照着搭一套。

3.1 数据模型设计

支教系统的核心模型,我建议至少包含这么几张表:

# teaching/models.py from django.db import models from django.contrib.auth.models import User class School(models.Model): name = models.CharField(max_length=100) region = models.CharField(max_length=50) school_type = models.CharField( max_length=10, choices=[("primary", "小学"), ("middle", "初中")], default="primary", ) student_count = models.IntegerField(default=0) need_subjects = models.CharField(max_length=200, blank=True) contact_name = models.CharField(max_length=20) contact_phone = models.CharField(max_length=20) created_at = models.DateTimeField(auto_now_add=True) class Volunteer(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) phone = models.CharField(max_length=20) skills = models.CharField(max_length=200) available_start = models.DateField(null=True, blank=True) available_end = models.DateField(null=True, blank=True) experience = models.TextField(blank=True) class TeachingProject(models.Model): title = models.CharField(max_length=120) school = models.ForeignKey(School, on_delete=models.CASCADE) subject = models.CharField(max_length=20) start_date = models.DateField() end_date = models.DateField() status = models.CharField( max_length=10, choices=[("draft", "草稿"), ("recruiting", "招募中"), ("ongoing", "进行中"), ("finished", "已结束")], default="draft", ) created_by = models.ForeignKey(User, on_delete=models.CASCADE) class Enrollment(models.Model): project = models.ForeignKey(TeachingProject, on_delete=models.CASCADE) volunteer = models.ForeignKey(Volunteer, on_delete=models.CASCADE) status = models.CharField( max_length=10, choices=[("pending", "待审核"), ("approved", "已通过"), ("rejected", "已拒绝")], default="pending", ) created_at = models.DateTimeField(auto_now_add=True) class CourseRecord(models.Model): project = models.ForeignKey(TeachingProject, on_delete=models.CASCADE) date = models.DateField() topic = models.CharField(max_length=200) student_count = models.IntegerField(default=0) content = models.TextField(blank=True)

设计的时候我特意加了region和school_type两个字段。前者让总队可以按片区筛选支教点,后者解决了前面说的小学部和初中部分开申报需求的问题。created_by字段记录项目发起人,后面做数据权限过滤会用到。

3.2 用 Django 管理后台打通业务闭环

如果你是 Django 新手,我建议把 Admin 后台先用起来。创建 app 之后,在admin.py里注册模型,运营人员就能直接维护数据了:

# teaching/admin.py from django.contrib import admin from .models import School, Volunteer, TeachingProject, Enrollment, CourseRecord admin.site.register(School) admin.site.register(Volunteer) admin.site.register(TeachingProject) admin.site.register(Enrollment) admin.site.register(CourseRecord)

注册完之后别急着说“完事了”。实际运营中,工作人员最常用的操作是“审核志愿者报名”,所以在后台里做两个优化:

  • 在EnrollmentAdmin里加list_filter = ("status", "project__school"),让运营人员能按项目和学校快速筛选待审核记录;
  • 给志愿者和学校列表加search_fields,比如按姓名、学校名、手机号搜索,这在数据量上来之后非常关键。

3.3 接口层:用 DRF 快速产出可用 API

系统接口我用了 Django REST Framework(DRF),因为它配好 ModelViewSet 之后,增删改查接口几乎不用写重复代码:

# teaching/api.py from rest_framework.viewsets import ModelViewSet from rest_framework.permissions import IsAuthenticated from .models import TeachingProject, Enrollment from .serializers import TeachingProjectSerializer, EnrollmentSerializer class TeachingProjectViewSet(ModelViewSet): queryset = TeachingProject.objects.all() serializer_class = TeachingProjectSerializer permission_classes = [IsAuthenticated] class EnrollmentViewSet(ModelViewSet): queryset = Enrollment.objects.all() serializer_class = EnrollmentSerializer permission_classes = [IsAuthenticated]

但有个实际问题是:不是所有已登录用户都能修改任何数据。比如某个志愿者不能把别人的报名记录改成“已通过”。所以我提了一个自定义权限类,只允许项目的创建者审核该项目的报名:

# teaching/permissions.py from rest_framework.permissions import BasePermission class IsProjectOwnerOrReadOnly(BasePermission): def has_object_permission(self, request, view, obj): if request.method in ("GET", "HEAD", "OPTIONS"): return True return obj.project.created_by == request.user

这里没有必要把权限做得很复杂,但要保证“每个人只能动自己的数据”这个底线。

对于文件上传,Django 侧主要在settings.py里配置好MEDIA_ROOT和MEDIA_URL,上传接口返回文件的绝对路径给前端。课程视频比较大,我只会把原始文件暂存,然后丢给 Flask 服务去转码,转码完成后再回调 Django 接口更新视频字段的地址。

4. Vue 前端落地:页面搭建、路由和接口对接

后端接口准备到位之后,前端才是让这套系统真正被用起来的关键。这里我分享一下用 Vue 搭建前端时的核心思路,尤其是一些和支教场景强相关的页面。

4.1 工程初始化和环境配置

创建前端工程,我建议直接使用npm create vue@latest生成基于 Vite 的项目。相比以前的 Vue CLI,Vite 启动速度快很多,尤其是在配置了代理的情况下改代码能秒级生效。

装依赖时别漏了这几样:vue-router、pinia、axios。很多刚上手的朋友会在 Pycharm 终端里敲npm install卡很久,这里其实是网络问题,可以考虑先用镜像源:

npm config set registry https://registry.npmmirror.com npm install

前端工程和后端分开两个目录,结构类似:

rural-teaching-system/ ├── backend # Django │ └── manage.py └── frontend # Vue ├── package.json └── src/

开发阶段的跨域问题,最省事的方案是在vite.config.js里配代理:

// vite.config.js import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { "/api": { target: "http://127.0.0.1:8000", changeOrigin: true }, "/media": { target: "http://127.0.0.1:8000", changeOrigin: true } } } });

这样前端代码里请求/api/teaching-projects/就能直接打到 Django 服务,不需要在 Axios 里写一长串地址,部署时也更方便切换环境。

4.2 核心页面拆解

我把页面分成三类角色:游客/志愿者、支教队运营人员、学校管理员。系统默认显示前端“首页 + 项目列表 + 单校详情”的公开信息。

  • 首页:展示近期招募中的支教项目卡片,点击进入详情页;
  • 项目详情页:展示学校信息、支教时间、需求科目,志愿者登录后可点击报名;
  • 报名页:填写个人可支教时间段、能教的科目、支教经历,提交后由运营人员在后台审核。

页面数量不多,但路由最好配置成支持动态参数的。热词里有“vue动态路由”,这里确实是它的典型场景——比如项目详情页的路径是/project/:id,不同 id 渲染不同数据:

// router/index.js import { createRouter, createWebHistory } from "vue-router"; const router = createRouter({ history: createWebHistory(), routes: [ { path: "/", name: "home", component: () => import("@/views/HomeView.vue") }, { path: "/project/:id", name: "projectDetail", component: () => import("@/views/ProjectDetail.vue") }, { path: "/my", name: "my", component: () => import("@/views/MyEnrollments.vue") } ] });

为什么不把所有页面都写成静态路由?因为支教系统后面还可能加“学校端管理”“总队看板”这类受限页面,如果用动态路由配合权限来控制菜单,就不需要每次上线都改路由表了。当然,第一版项目也可以从简单开始,我给的例子适合入门后继续演进。

4.3 Axios 封装与文件上传

接口请求如果每个页面都写一遍axios.get,后续维护成本会很高。我习惯封装一个request.js:请求拦截器自动带上 JWT token,响应拦截器统一处理登录过期。

// utils/request.js import axios from "axios"; const request = axios.create({ baseURL: "/api", timeout: 15000 }); request.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); request.interceptors.response.use( (response) => response.data, (error) => { if (error.response && error.response.status === 401) { // 跳转登录页或刷新 token } return Promise.reject(error); } );

文件上传这里,很多人习惯直接走<input type="file">,但在支教系统里,运营人员和老师往往是在会议室、教室用旧电脑上传,网络环境一般,所以我建议在 axios 里做上传进度条。用一个FormData把文件传上去,配合onUploadProgress显示进度,体验会好很多。

课程视频这块,原始文件很大,我前面的 Flask 服务会转成多个 ts 切片文件和.m3u8索引文件。前端播放时,用 hls.js 就能直接播:

import Hls from "hls.js"; function playVideo(url, videoElement) { if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource(url); hls.attachMedia(videoElement); } else if (videoElement.canPlayType("application/vnd.apple.mpegurl")) { videoElement.src = url; } }

在 PC 上 hls.js 的兼容性很好,在手机自带的 Safari 上也可以因为支持 HLS 直接播放,所以这套方案在乡村支教点常见的安卓平板上也能跑通。

5. Pycharm 里的开发全流程:从环境配置到前后端联调

这一章结合热词里频繁出现的“pycharm安装教程”“pycharm配置python环境”“django创建app”这些搜索点,分享我在这套项目里实际摸出来的流程和坑。

5.1 环境准备:Python 解释器和虚拟环境

用 Pycharm 新建 Django 项目时,我一般会选New environment using Virtualenv,Python 版本选 3.10 或 3.11。这里有个容易踩坑的地方:如果你本机装了多个 Python 版本,Pycharm 默认会选系统 PATH 里靠前的那一个,但你可能需要的是另一个版本的解释器。

解决办法:在设置里手动指定解释器路径,比如C:\Python311\python.exe,虚拟环境继承它。建立好之后,用python --version确认一下版本,别等到makemigrations因为语法不兼容报错才发现。

装依赖的命令我习惯在 Pycharm 的 Terminal 面板里执行,因为这个终端会自动激活虚拟环境,命令前面不需要再手动venv\Scripts\activate:

pip install django djangorestframework djangorestframework-simplejwt django-cors-headers pandas

热词里有朋友搜“pycharm怎么安装pandas包”,其实在这里用pip install pandas就行。如果网络比较慢,也可以先在设置里配置国内镜像源,再安装。

5.2 创建 app 和跑起第一个接口

热词里“django创建app”也很常见。流程是:

python manage.py startapp teaching

然后在settings.py的INSTALLED_APPS里加上teaching。这里提醒一个坑:新增 app 后如果不重启开发服务器,migrate时可能出现找不到模型的奇怪问题,所以尽量在新增 app 之后重启一下 Pycharm 里的 Django Server。

在 Pycharm 中配置运行方式:打开“Edit Configurations”,点加号选 Django Server,设置settings模块路径。这样点一下绿色三角就能直接跑起来,断点调试也非常方便。

5.3 用 Pycharm 的 HTTP Client 做接口测试

前后端联调之前,我先用 Pycharm 自带的 HTTP Client 把接口都测一遍。新建一个api_tests.http文件,可以直接在编辑器里点击发送请求:

### 获取 token POST http://127.0.0.1:8000/api/token/ Content-Type: application/json { "username": "volunteer01", "password": "test123456" } ### 创建支教项目 POST http://127.0.0.1:8000/api/teaching-projects/ Content-Type: application/json Authorization: Bearer <token> { "title": "春季乡村小学趣味数学课", "school": 1, "subject": "math", "start_date": "2025-03-01", "end_date": "2025-06-30", "status": "recruiting" }

这个方法比在浏览器里调试直观多了。如果请求返回 500,直接看 Pycharm 终端里的 Traceback,哪里崩了一目了然。新手在这里最大的问题是不看报错就到处问,其实 90% 的问题都写在 Traceback 里。

5.4 实际开发中遇到的几个典型问题

说三个我真实踩过的坑,都是搜索引擎里高频出现的问题:

  • 端口被占用:跑 Django 开发服务器时提示 8000 端口被占用,特别是之前开过python manage.py runserver没有正常关闭。我一般是先netstat -ano | findstr :8000看是哪个进程占用,然后任务管理器里结束进程,再重新运行。
  • Vue 热更新不生效:Pycharm 里跑npm run dev之后改了代码页面不刷新。这种情况多数是因为 Vite 的默认监听端口和代理配置冲突,检查一下是不是同时开了多个前端 dev server。
  • 前后端时间字段不一致:Django 默认使用 UTC 时间,前端页面显示的支教时间比本地时间差 8 小时。解决办法是在settings.py里设置TIME_ZONE = "Asia/Shanghai",并且USE_TZ = False(如果项目不涉及多时区用户,这个配置最简单直接)。

6. 部署、推广与后续扩展:让系统真正被用起来

开发完成只算走了一半,系统最终要部署到服务器上给支教队用,才算真正落地。

6.1 简化的部署方案

对于中小规模项目,我不推荐一上来就上容器编排那套。最简单可靠的组合是:

  • Django + Gunicorn 跑主服务;
  • Nginx 监听 80 端口,转发 API 请求到 Gunicorn,同时托管 Vue 打包后的静态文件;
  • Flask 服务独立跑在 9000 端口,专门处理转码和导出;
  • SQLite 或小规模 MySQL 都够用,数据量不大,优先选 SQLite,备份就是一个文件。

前端构建:

npm run build

生成的dist目录放到服务器 Nginx 的站点根目录,同时配置:

location / { try_files $uri $uri/ /index.html; } location /api { proxy_pass http://127.0.0.1:8000; }

try_files那行是为了让 Vue 的路由在刷新页面时不出现 404,这个配置经常被新手忽略,项目一部署刷新后白屏,多半就是缺了它。

6.2 系统上线后的功能迭代方向

第一版上线后,如果运营稳定,后续可以在不伤筋动骨的情况下做这些扩展:

  • 课时统计看板:把 CourseRecord 的数据按学校、科目、月份聚合,做成可视化报表。这里就能用上前面的pandas做数据处理,再输出给前端 ECharts 展示。
  • 支教课程资源的在线预览:除了视频,还可以支持 PDF 课件预览。热词里有人问“vue image能显示pdf吗”,这个不是 image 标签能直接解决的,一般会用pdf.js或者服务端转图片再预览。
  • 移动端轻量化适配:乡下学校的老师普遍用手机访问,所以前端的项目详情、报名、课时录入页面必须做响应式适配。Vue + Vant 或者普通 CSS 媒体查询都行,核心是别让老师上课间隙点半天按钮。

6.3 给正在复现这套系统的人一个经验总结

如果你照着这个思路自己搭一套,我建议第一版严格控制在“学校信息、志愿者信息、项目发布、线上报名、课时记录”这五个功能内,别急着加积分商城、直播课堂、AI 课程推荐这些花哨能力。先把主流程跑通,让支教队的运营人员真的用起来,再根据他们的反馈迭代。

我个人的体会是,这类系统能不能活下来,关键不在技术用了多新的框架,而在于业务方是否愿意每天打开它。所以开发阶段多花点时间在后端 Admin 的字段筛选、前端页面的加载速度、上传视频的转码流畅度这些体验细节上,远比多做几个概念功能有价值。等这套流程稳定了,再往其他乡村教育协作场景复制,就只是换皮和改字段的事了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 1:58:02

陶瓷勺做淘宝商品图:勺柄弧度和勺口先保真,AI设计只承担准确布局

陶瓷勺商品图可以先整理授权原图和可靠资料&#xff0c;再用剪映当前图片设计能力试布局。AI背景或排版不能把勺柄、勺口和实际器型改成新款&#xff1b;商品事实与当前发布显示要求&#xff0c;都需要独立核准&#xff0c;不能从一张漂亮设计图推断。本文没有真实商品图效果测…

作者头像 李华
网站建设 2026/10/17 14:04:49

机票+火车票聚合预订系统实践:多源数据一致性与稳定性架构复盘

做机票火车票聚合查询与预订系统&#xff0c;难的不是把航班和车次拼到一个列表里&#xff0c;而是要把两套完全不同的数据源、交易链路和业务规则&#xff0c;揉成一个对用户“无感”的出行产品。我复盘了从0到1搭建这套系统的全过程&#xff0c;包含技术选型、接口适配、缓存…

作者头像 李华
网站建设 2026/10/17 18:13:40

aarch64 Linux上Eclipse 2021-12 R C/C++开发环境部署与调试指南

简介&#xff1a;面向 C/C 开发者的 Eclipse IDE 2021-12 稳定版&#xff08;代号 R&#xff09;&#xff0c;专为 64 位 Linux aarch64 架构设计&#xff0c;基于 GTK 图形界面&#xff0c;预装 CDT 工具链并集成 GCC/Clang 编译、GDB 调试、Git 版本控制、性能分析等核心功能…

作者头像 李华
网站建设 2026/10/17 18:48:28

轻量可靠UDP协议UEC:面向边缘设备的通信新范式

1. 项目概述&#xff1a;为什么边缘设备需要一个“不挑食”的UDP协议&#xff1f;最近在给某高校物联网实验室做边缘网关通信模块优化时&#xff0c;反复被一个问题卡住&#xff1a;几十台部署在工厂车间、农业大棚、城市井盖下的嵌入式设备&#xff0c;用标准UDP发心跳包&…

作者头像 李华
网站建设 2026/10/17 14:43:13

AnyPS5:PS5非官方能力拓展的系统级方法论

项目标题&#xff1a;“AnyPS5”这个名称一出现&#xff0c;我就下意识多看了两眼——不是因为它是某个新发布的硬件产品&#xff0c;也不是某家大厂的官方代号&#xff0c;而是因为它精准踩中了当前玩家圈里一个真实、高频、且长期被模糊处理的需求&#xff1a;“我手里的PS5&…

作者头像 李华