每年公考报名季,高校就业指导中心的老师和我聊得最多的一个词就是“信息太散了”。国考、省考、事业单位、人才引进,公告撒在几十个官方渠道里,学生要么错过报名时间,要么把备考资料存得到处都是。我自己在带毕设时也发现,如果能做一个把招考资讯、辅导课程、政策通知整合在一起的平台,不管是做毕业设计还是真正给学院用,都非常有价值。今天要聊的这套源码,就是干这个事的:Python Flask 写后端接口,Vue 写前端页面,数据库用 MySQL,开发环境是基于 PyCharm 搭建的,功能上覆盖了资讯发布、公告管理、辅导资源展示、后台管理等常见模块。
这套项目源码的标题里同时挂着 flask 和 django 两个关键词,实际看了目录结构之后发现主体是 Flask 工程,附带了一个 Django 风格的管理模块作为对照参考。打包资源的人为了让搜索命中率高,把两个框架的名字都写进去了,这也正常。如果你正准备找毕设项目,或者想用 Flask + Vue 完整练一遍前后端分离开发,这个项目可以当主线参考。下面我按实际开发顺序,从需求拆解到环境搭建、接口实现、上线部署,把整套源码里值得关注的地方都说一遍。
1. 项目整体需求拆解与功能规划
1.1 这个系统到底在解决什么问题
高校毕业生在准备公职考试时,信息获取路径非常零散。招录公告要看政府官网,岗位表要下 Excel,备考资料散落在公众号和网盘,辅导课程又分布在各个机构平台上,学生要同时维护十几个入口,很容易漏掉关键时间节点。
这套系统的核心逻辑,是把“公职资讯聚合 + 辅导资源管理”放在一个平台里。用户登录后可以看到按分类整理的招考资讯,可以浏览辅导课程和公开课,可以下载备考资料,还能收藏自己感兴趣的内容。管理员在后台维护分类、发文、管理课程和用户,整个信息流转比手工整理要高效得多。
从源码的目录结构来看,作者采用了典型的前后端分离方式,后端提供 JSON 接口,前端通过 HTTP 请求来消费接口数据。这种设计的好处是前后端可以并行开发,后端调整不影响前端页面,以后不管换 Web 端还是做小程序,接口可以直接复用。
1.2 源码里的功能模块清单
把项目跑起来之后,可以看到功能模块划分得比较清晰。我整理了一个功能表,方便对照源码理解:
| 模块分类 | 使用角色 | 核心功能 |
|---|---|---|
| 用户端 | 学生、毕业生 | 注册登录、个人资料、资讯浏览、公告通知、辅导课程浏览、资料下载、收藏 |
| 管理端 | 管理员 | 资讯分类管理、资讯发布与编辑、公告管理、课程管理、用户管理、数据统计 |
| 系统基础 | 公共部分 | 图片上传、JWT 登录鉴权、分页搜索、异常处理 |
这些功能看起来不复杂,但实际编码时涉及路由规划、权限校验、文件上传、跨域处理、数据库关联查询等问题。比如资讯列表不仅要分页,还要支持按分类筛选、按标题模糊搜索、按置顶状态排序,这条链路任何一个环节没打通,前端页面显示就会出错。
1.3 为什么选这个功能组合
做这类项目最容易犯的错是功能堆砌。很多毕设源码上来就是购物车、订单、支付,和业务场景完全脱节。这套源码明显是克制了,只保留了资讯系统和辅导系统最核心的部分。
从用户使用路径来看,一个学生来到平台,第一件事是看最新政策公告和招考资讯,然后根据岗位信息去查对应备考资料,再决定要不要参加辅导课程。资讯、课程、资料、收藏四个模块刚好串起这条链路。管理端做的事情则是保证内容能持续更新,没有管理员维护的资讯平台,上线一周就会变成死站。
这个功能组合也决定了技术实现的复杂度比较适中,既没有避开数据库设计和前后端交互,又没有刻意去碰分布式、消息队列这些和业务不匹配的东西。对拿来做毕业设计或者学习范例来说,是很好的度。
2. 技术栈选型思考:Flask、Vue 与 Django 怎么搭配
2.1 Flask 赢在“轻”,Django 赢在“全”
很多人在选 Python Web 框架时都会纠结 Flask 还是 Django。我的看法是:项目规模和团队习惯决定选型。Django 自带 Admin 后台、ORM、表单处理、认证体系,项目骨架非常完整,适合开发大型业务系统;但它的工程结构和模块耦合也相对重,初学者想完全搞明白并不容易。
Flask 则是一个微内核框架,核心只提供请求路由和模板渲染,数据库、登录、表单全都可以按需加载。这个系统需要的就是一套轻量接口服务和灵活的数据模型,用 Flask 后代码结构一目了然:app.py 负责启动,models.py 管数据表,routes 目录管不同业务的路由。开发和调试都比 Django 直接。
当然,Django 也不是没有参考价值。这套源码里附带的 Django 模块演示了如何用 MTV 模式下自带 Admin 快速生成后台管理页,相当于给了一个对照组。我在实际开发中的选择是:如果后台管理页面要求不高,直接用 Django Admin 能省掉大量 CRUD 编码;如果要自定义各种业务细节,Flask + Flask-Admin 或者完全手写管理页反而更灵活。
2.2 前端为什么用 Vue
前端技术栈选择 Vue 是挺顺理成章的事。Vue 的核心优势是组件化和响应式数据绑定,页面上的资讯列表、分页器、导航栏都能拆成独立组件,复用性很好。和 Flask 后端之间通过 JSON 数据交换,完全不依赖服务端模板,Vue 和 Flask 的开发可以同时推进。
从上手难度来说,Vue 比 React 更接近传统 HTML 的书写习惯。一个资讯列表页面,模板里写 v-for 循环渲染数据,脚本里调用 axios 拉接口,不需要理解复杂的状态管理概念。项目源码里用到的组件和路由配置也比较常规,没有刻意上 Vuex 或者 Pinia,这套模式很适合拿来学习。
另外,Vue 的构建工具在最新版本里已经切换到了 Vite,启动速度快很多。如果你下载的源码还是老版本的 Vue CLI 配置,我也想多说一句:不要把时间花在纠结版本上,核心原理是一样的,都是组件 + 路由 + HTTP 请求。
2.3 开发工具链:PyCharm 配合整个流程
后端开发在 PyCharm 里进行是最顺手的。PyCharm 对 Flask 项目有原生支持,可以一键创建 Flask 项目模板,自带虚拟环境管理,运行配置还能自动识别 app.py 入口。写代码时对 Flask 路由模版的补全、断点调试、SQL 语句提示都比通用编辑器要智能很多。
版本选择上,社区版是免费的,日常写 Flask 完全够用;专业版多了数据库工具、前端代码检查等功能,学生可以申请教育免费授权。我不建议去碰网上那些激活脚本,一来安全隐患很大,二来你为省一点订阅费把自己电脑暴露风险,完全不划算。老老实实装个社区版或者走正规授权,项目一样能跑得飞起。
环境还需要 MySQL 和 Node.js。MySQL 用来存数据,Node.js 是运行前端开发服务器和打包工具的基础。Windows 下安装 MySQL 有一点要特别注意:安装时记住 root 密码,如果用 MySQL 8.x,还可能在导入 sql 文件时遇到认证插件兼容问题,后面我会专门讲。
3. 数据库设计、后端接口与前端页面如何打通
3.1 数据表结构是整套系统的地基
拿到源码不要急着跑,先看数据库设计脚本,或者直接打开 models.py。这个系统的表结构设计得很典型,我挑几张核心表来说明。
用户表 user 字段包括 id、username、password_hash、phone、avatar、role、create_time。这里密码字段用的是 hash 值而不是明文,虽然代码里可能只演示了注册功能,但这个习惯必须养成。明文密码存数据库,一旦泄露就是安全事故。
资讯相关的表有两张,category 和 article。category 是分类表,里面有 id、name、sort;article 是资讯主表,字段有 id、category_id、title、cover、summary、content、source、views、is_top、publish_time。把分类和文章拆成两张表是为了避免在一张表里反复写一串相同的分类名,后续想统计某类职位资讯的数量也会很方便。
辅导相关的表包括 course、course_category 等,course 表设计时把 video_url、file_url、teacher、intro 都放进去,这样前端展示课程卡片时只需要一次查询就能拿到全部展示信息。还有一张 favorite 收藏表,用 target_type 字段来区分收藏的是资讯还是课程,这是一种很常见的“多态关联”写法,可以省去为每种业务单独建一张收藏表的麻烦。
3.2 用 Flask 写一个资讯列表接口
后端代码最核心的一段是资讯查询接口。我简化一下源码里的写法,大致是这样:
from flask import Blueprint, request, jsonify from models import Article, db from sqlalchemy import or_ article_bp = Blueprint('article', __name__) @article_bp.route('/api/articles', methods=['GET']) def get_articles(): # 接收查询参数 page = request.args.get('page', 1, type=int) limit = request.args.get('limit', 10, type=int) keyword = request.args.get('keyword', '', type=str) category_id = request.args.get('category_id', 0, type=int) # 构建查询条件 query = Article.query if keyword: query = query.filter(or_(Article.title.like(f'%{keyword}%'), Article.summary.like(f'%{keyword}%'))) if category_id: query = query.filter(Article.category_id == category_id) # 排序与分页 pagination = query.order_by(Article.is_top.desc(), Article.publish_time.desc()) \ .paginate(page=page, per_page=limit, error_out=False) # 组装返回结果 items = [] for item in pagination.items: items.append({ 'id': item.id, 'title': item.title, 'cover': item.cover, 'summary': item.summary, 'category_id': item.category_id, 'views': item.views, 'publish_time': item.publish_time.strftime('%Y-%m-%d %H:%M:%S') }) return jsonify({ 'code': 0, 'data': items, 'total': pagination.total, 'page': page, 'limit': limit })这里有几个细节值得注意。分页用 SQLAlchemy 的 paginate 方法来处理,比自己手算 offset 更安全,还能顺带拿到 total 总数返回给前端渲染分页条。排序时用 is_top 字段做降序,再按 publish_time 降序,这样置顶文章始终在列表前面。关键字搜索用 LIKE 匹配标题和摘要,虽然大数据量下性能一般,但对这套系统来说完全够用。
接口返回格式统一用 code + data + total 的结构,前端拿到后直接判断 code 是否为 0,不需要再包一层 try catch 去猜后端返回格式,联调时可以省掉很多沟通成本。
3.3 前端 Vue 如何消费后端接口
Vue 端拿到这样的接口,处理起来很直接。我以资讯列表页为例,核心流程是:页面加载时调用 axios 请求 /api/articles,把返回的 items 数组渲染到模板里,表格下方放分页组件,请求参数里带上 page 和 limit。
import axios from 'axios' export default { data() { return { articleList: [], total: 0, page: 1, limit: 10 } }, created() { this.loadArticles() }, methods: { async loadArticles() { const res = await axios.get('/api/articles', { params: { page: this.page, limit: this.limit, keyword: this.keyword, category_id: this.categoryId } }) if (res.data.code === 0) { this.articleList = res.data.data this.total = res.data.total } } } }模板里用 v-for 循环渲染 articleList,分页组件绑定 page 和 total,切换页码时重新调用 loadArticles。这一步只要能打开浏览器看到数据,就说明前后端已经打通了。
开发阶段还有一个避不开的问题:Flask 默认跑在 5000 端口,Vue 开发服务器跑在 5173 或 8080 端口,两者不同源,浏览器会拦截跨域请求。解决方式有两种,第一种是在 Flask 端加 flask-cors 放行所有域名,适合快速联调;第二种是在 Vue 的 vite.config.js 里配置 devServer.proxy,把 /api 开头的请求代理到后端地址,生产环境再由 Nginx 统一转发。我建议正式项目用第二种,因为代理方式更贴近真实部署环境。
4. 从 0 到 1 在 PyCharm 里跑通这套源码
4.1 环境准备:Python、MySQL、Node.js 一个都不能少
先把底座搭好。Python 建议装 3.8 以上版本,我自己测试时用的是 3.10,Flask 2.x 和 SQLAlchemy 都能正常工作。安装时 Windows 系统要记得勾选 Add Python to PATH,否则命令行里敲 python 会提示找不到命令。
PyCharm 安装好之后,直接选择 Open 打开下载的源码目录,PyCharm 会自动识别到 requirements.txt,并提示创建虚拟环境,点确定即可。如果提示没有识别到项目类型,建议检查一下项目根目录下是否有 app.py 或 wsgi.py,Flask 项目入口在那个位置。
MySQL 安装时,5.7 和 8.0 我都试过,建议用 8.0,功能更新而且字符集支持更好。安装过程中设置一个你能记住的 root 密码,后续连接数据库需要用到。安装完成后用 Navicat 或命令行建一个名为 gongzhi 的数据库,再把源码里的 gongzhi.sql 文件导入,表结构就有了。
Node.js 安装则比较简单,一路默认即可。安装完成后打开终端输入 node -v 和 npm -v,能输出版本号就算成功。
4.2 后端启动步骤与数据库配置
后端项目在 PyCharm 里打开后,先确认虚拟环境已经激活。然后打开终端,安装依赖:
cd backend python -m venv venv venv\Scripts\activate pip install -r requirements.txt如果没有 requirements.txt,也可以手动安装:pip install flask flask-sqlalchemy flask-cors pymysql flask-migrate flask-jwt-extended。
安装完依赖后,修改 config.py 里的数据库连接信息。常见的连接串写法是:
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:yourpassword@127.0.0.1:3306/gongzhi?charset=utf8mb4"这里的 yourpassword 换成你本机 MySQL 的 root 密码。如果连的是远程数据库,把 127.0.0.1 换成对应 IP。设置完成后,运行 app.py,控制台会输出 Running on http://127.0.0.1:5000,说明后端服务起来了。
一个容易踩的坑是:MySQL 8.0 默认认证插件是 caching_sha2_password,而旧版 PyMySQL 可能不支持。解决办法是把数据库用户认证插件改成 mysql_native_password,或者在配置里装上最新版 PyMySQL,一般后者就够了。
4.3 前端项目启动与跨域代理配置
前端目录一般叫 frontend 或 vue-front,进入目录后:
cd frontend npm install npm run devnpm install 时间较长,这期间可以先配置跨域代理,不然等接口联调时再回头改,又要浪费几分钟。vite 配置文件里加一段 server.proxy:
server: { proxy: { '/api': { target: 'http://127.0.0.1:5000', changeOrigin: true } } }如果你的源码是 Vue CLI 项目,则在 vue.config.js 里配置 devServer.proxy,原理一样。配置好后重启 npm run dev,页面里就能直接请求后端接口了。
前端默认开发端口常见的是 5173,浏览器打开后如果页面白屏,先看终端有没有报错。大多数情况是依赖没装完整,或者 Node 版本太低,建议 Node 不要低于 16。
4.4 依赖安装常见报错速查
我把源码运行过程中最常见的几个依赖问题整理成一个速查表,遇到直接照着做:
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
| ModuleNotFoundError: No module named 'MySQLdb' | 用了 MySQLdb 但没装 | pip install pymysql,并在 app 中添加 import pymysql; pymysql.install_as_MySQLdb() |
| pip 安装超时 | 访问官方源太慢 | 换镜像:pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple |
| npm ERR! code ERESOLVE | 依赖版本冲突 | npm install --legacy-peer-deps |
| node-sass 安装失败 | 老项目中 node-sass 和 Node 版本不兼容 | 用 sass 替代,或升级 Node 后再试 |
| No module named 'flask_wtf' | 缺少表单扩展 | pip install flask-wtf |
这里特别提一下 pymysql 替换 mysqlclient 的方法,很多源码写的是 import MySQLdb,因为这个接口是从 mysqlclient 来的。装上 PyMySQL 后,只要在项目入口文件加两行代码,就可以兼容运行。这招在 Windows 上尤其好用,因为 mysqlclient 在 Windows 下编译经常失败。
5. 上线部署:本地能跑只是第一步
5.1 Flask 应用生产部署:Waitress + Nginx
开发环境下,python app.py 启动的是 Werkzeug 自带的开发服务器,它有自动重载和调试页面,但并发能力很差,生产环境绝对不能直接用到公网。Windows 服务器上最常见的组合是 Waitress + Nginx。
Waitress 是纯粹的 Python WSGI 服务器,安装和启动都很简单:
pip install waitress waitress-serve --host 0.0.0.0 --port 8000 app:app注意 app:app 前面是 Python 文件名,后面是 Flask 实例名,如果入口文件是 run.py,并且里面的应用变量叫 application,那就写成 run:application。启动后后端就监听在 8000 端口。
Nginx 负责接收外部请求,把静态资源直接返回,把带 /api 的请求转发给 Waitress,同时处理 Vue 打包后的前端文件。一个简单的配置片段:
server { listen 80; server_name your_domain.com; root /var/www/gongzhi_frontend/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }这段配置里 try_files 是 Vue history 路由必须的。前端路由切到 /article/1,浏览器刷新时请求到服务端,Nginx 找不到这个物理路径就直接返回 index.html,再由 Vue 路由接管页面,否则刷新就是 404。
5.2 Vue 打包后的两个大坑
Vue 开发模式一切正常,npm run build 打出来的包放到服务器上却经常出问题。第一个坑是静态资源路径不对,默认的 base 是反斜杠根路径,如果你把前端放在域名子目录下,就得在 vite.config.js 里设置 base: './',让资源路径变成相对路径,或者部署时单独配置。
第二个坑正好对应很多人在搜的“vue 打包后布局异常”。这类问题大多是两种原因:一是本地调试时用了某个全局样式或 Element UI 组件,但打包后 CSS 被压缩产生了覆盖问题;二是路由跳转后,页面容器高度塌陷,导致底部留白或板块错位。排查时先用开发者工具看盒模型,再看样式来源,通常能定位到是某个全局样式没有加载进来。
还有一点,部署后 API 地址如果是写死的 http://127.0.0.1:5000,那生产环境一定调不通。源码里一般会把 API 地址抽成一个环境变量或配置文件,打包时要改成实际域名对应的 Nginx 地址。
5.3 数据备份与迁移
线上系统跑起来以后,备份就是你最不能省的事。Flask 项目开发阶段可以用 Flask-Migrate 管理表结构变更,流程是:
flask db init flask db migrate -m "init tables" flask db upgrade上线之后,每天凌晨用 mysqldump 做一次全量备份是基本操作:
mysqldump -uroot -p --default-character-set=utf8mb4 gongzhi > /backup/gongzhi_$(date +%Y%m%d).sql恢复时执行 mysql -uroot -p gongzhi < backup.sql。这套操作我在实际项目里踩过坑,一开始没加 --default-character-set 参数,备份文件里中文注释全部乱码,恢复后数据看起来像一堆问号。所以备份命令里的字符集参数千万别省略。
6. 常见报错与排坑实录
6.1 前后端联调被跨域卡住
Flask 后端和 Vue 前端端口不同,浏览器控制台报错 Access-Control-Allow-Origin missing,第一反应就是跨域。如果你不想在前端做代理,直接在 Flask 里加 flask-cors 是最省事的:
from flask_cors import CORS CORS(app)这样所有的接口都会带上跨域响应头,开发环境没问题。但上线后建议只开放指定域名,避免接口被任意第三方页面乱调用。写法是 CORS(app, resources={r"/api/*": {"origins": "http://yourdomain.com"}})。
我遇到过一次很有意思的情况:加了 CORS 还是报跨域。查了半天发现是浏览器端用了自定义请求头 x-token,触发了预检请求,而预检请求被 Flask 的某些路由拦掉了。最后在 CORS 配置里明确支持所有请求头才解决,如果你的接口需要自定义 header,记得也把 allow_headers 配好。
6.2 中文乱码从数据库到前端层层排查
中文乱码问题在 Flask + Vue 项目里出现概率很高。排查顺序是:先看数据库里的原始数据是否正常,再用命令行请求 API 看返回内容,最后检查浏览器渲染。
数据库插入前要保证建库时指定了 utf8mb4 字符集,连接串里也带 charset=utf8mb4。如果数据已经变成问号写进表里了,基本没法修复,只能删掉重来。返回 JSON 时,Flask 的 jsonify 默认会把中文转成 Unicode 编码,这不是乱码。如果不想让前端看到转义序列,可以在 Flask 配置里设置 JSON_AS_ASCII=False。
前端页面如果显示乱码,检查 index.html 有没有设置,以及 JS 文件是否正确加载。前后端编码全部统一成 UTF-8 之后,这类问题基本灭绝。
6.3 后台管理插件怎么选
热搜词里出现的“flask 后台管理插件”,指的大概率是 Flask-Admin。这个插件可以在几分钟内生成一套可用的后台管理界面,只需要定义好你的模型类,然后注册进去:
from flask_admin import Admin from flask_admin.contrib.sqla import ModelView admin = Admin(app, name='公职资讯后台') admin.add_view(ModelView(Article, db.session))它能自动生成列表、新增、编辑、删除页面,对于纯管理表数据的需求,开箱即用。但它的硬伤是对自定义页面支持很弱,比如你想做一个富文本编辑器,加一个图片上传组件,或者自定义统计图表,Flask-Admin 就有点吃力了。
这套源码里的后台是自己写的前后端分离实现,前台 Vue 管理页面,后端写接口,灵活性更高。我的建议是:如果你赶时间,可以用 Flask-Admin 先顶一段时间;如果你要把它当毕设展示,自己写管理页面更能体现完整度。
6.4 看到 Django 词条,别被带偏
源码描述词里出现了 django,但主工程里并没有用 Django。很多资源站为了在搜索时覆盖更多关键词,会把同一类框架的名字都挂上去,这并不代表代码里都用了。
不过这里可以顺便说一下 Django 的 MTV 模式。M 是 Model,负责数据表映射;T 是 Template,负责页面展示;V 是 View,负责业务逻辑。和 Flask 相比,Django 把这三个层次直接内置到框架里,开发时“约定优于配置”的味道更重。如果你先学了 Flask 再看 Django,会觉得它的目录结构非常规整,但代价是很多环节 Django 已经帮你做完了决策,个性化时会觉得不够自由。
实际选型时,如果项目规模不大,团队又熟悉 Flask,完全没必要为了“大而全”硬上 Django。这套源码把两者放在一起,更多是给学习者提供一个对照参考。
6.5 开发环境里的其他小坑
有朋友安装完 PyCharm 后 Flask 项目跑不起来,第一反应是代码有问题,其实很多时候是运行配置里的入口脚本选错了。PyCharm 里 Add Configuration,选择 Flask Server,Target 指向 app.py,环境变量里设置 FLASK_ENV=development,就能正常启动。如果你在命令行里启动没问题,在 PyCharm 里启动报错,优先看虚拟环境和工作目录配置。
还有一点:虚拟环境一定要在项目目录里。很多时候 import flask 报 ModuleNotFoundError,但命令行里明明已经装过依赖,就是因为 PyCharm 解释器指向了全局环境而不是项目下的 venv。修改位置在 File -> Settings -> Project -> Python Interpreter,手动选到 venv 下的 python.exe 即可。
整套源码我前后改了三版才觉得逻辑比较顺,过程中最深的体会是:前后端分离的项目,真正耗时间的不是写代码,而是把数据模型、接口约定、页面状态之间对接清楚。建议拿到源码后不要急着跑,先把数据库表结构和接口文档过一遍,能少踩很多坑。后续想扩展的话,可以在资讯模块加上关键词检索和按专业分类的个性化推荐,把辅导课程按直播、录播、题库拆成更细的资源类型,再给管理员加一个简单的数据看板。这个项目底子打得不错,扩展空间很大。