首页 / 资讯中心 / 文章详情

Flask全栈笔记系统:高分毕设与生产级部署实战

Flask全栈笔记系统:高分毕设与生产级部署实战 ★ FEATURED ARTICLE
简介这是一套基于PythonFlask框架开发的在线笔记系统毕业设计项目面向计算机类专业本科生及初学者解决课程设计、毕业设计选题与快速原型开发需求。项目已通过完整功能测试涵盖用户注册登录、笔记增删改查、分类管理等核心模块适合作为软件工程、人工智能、电子信息等专业的毕设参考、课设实践或自学进阶范例。压缩包共46个文件含5个关键Python后端逻辑文件如app.py、models.py、14个HTML前端模板、3个JS交互脚本、6个界面截图PNG及2个Windows批处理启动脚本辅以CSS样式、配置文件与虚拟环境说明整体仅610KB轻量易部署。目前已有155人学习下载资源附带详细使用文档目录结构清晰含templates、static、venv等标准Flask组织方式并保留__pycache__与.idea等开发痕迹便于理解项目初始化与调试流程。1. 这不是又一个“Hello World”Flask项目它真能当主力笔记工具用且毕业答辩老师当场追问了三个部署细节你搜“Flask 毕业设计”页面刷出来十页全是带登录页的空壳系统——点开源码app.py里只有路由和render_template数据库连sqlite3.connect()都没封装更别说用户权限、富文本编辑、标签管理这些真实场景刚需。但这个项目不一样它跑起来就是个能写 Markdown、自动保存草稿、支持多级文件夹标签分类、导出 PDF 的完整在线笔记系统后端用 Flask SQLAlchemy Flask-Login Flask-WTF前端是 Vue 2.6 Element UI非纯模板渲染所有接口都走 RESTful 风格连/api/notes/search?qxxx这种带分页和模糊匹配的接口都已实现。它不是为“交差”写的是为“能用”写的——我拿它替换了自己用了三年的本地 Obsidian 同步方案在 4 核 8G 的轻量云服务器上稳定跑了 5 个月日均 200 请求无报错。适合两类人一是计算机/软件工程专业正在找高分毕设选题的同学答辩材料齐全含需求分析文档、ER 图、接口文档、部署手册、答辩PPT二是想快速验证 Flask 全栈开发闭环的工程师Vue 前端可独立编译后端可一键启动不依赖 Docker 或复杂中间件。2. 从解压到首页渲染三步跑通全链路关键在config.py和requirements.txt的隐性约定2.1 解压即得完整工程结构看清目录层级才能避开“找不到 static”的玄学报错解压后你会看到标准 Flask 项目骨架但有两处与教学示例不同online-note-system/ ├── app/ # 主应用包非单文件 │ ├── __init__.py # 创建 Flask 实例 初始化扩展 │ ├── models.py # SQLAlchemy 模型Note, Tag, Folder, User │ ├── routes/ # 路由模块化auth/, api/, main/ │ │ ├── __init__.py │ │ ├── auth.py # 登录/注册/密码重置含邮箱验证模拟 │ │ ├── api.py # 所有 /api/ 接口JSON 响应含错误码规范 │ │ └── main.py # 前端路由入口/dashboard, /note/123 │ ├── static/ # Vue 编译后产物注意不是源码 │ │ ├── css/ │ │ ├── js/ │ │ └── index.html # Vue SPA 入口关键Flask 只 serve 这个 │ └── templates/ │ └── base.html # 仅用于 Flask-WTF 表单 CSRF token 注入 ├── migrations/ # Flask-Migrate 生成的数据库迁移脚本 ├── config.py # 核心配置开发/生产环境切换开关 ├── requirements.txt # 明确指定版本Flask2.3.3, SQLAlchemy2.0.23 ├── run.py # 启动入口调用 app.create_app() └── README.md # 包含数据库初始化命令和默认账号提示static/目录下是 Vue 编译后的静态资源不是 Vue 源码。项目交付的是“可直接运行的成品”不是“需要你再 npm install 的开发态”。这点必须认清否则你会浪费 2 小时试图在static/js/里找.vue文件。2.2 数据库初始化别跳过flask db upgrade否则登录页永远卡在 500项目使用 Flask-Migrate 管理数据库变更不能直接sqlite3 notes.db手动建表。正确流程是# 1. 安装依赖确保 Python 3.8 pip install -r requirements.txt # 2. 设置环境变量Windows 用 setLinux/macOS 用 export export FLASK_ENVdevelopment export FLASK_APPrun.py # 3. 初始化数据库关键这一步生成初始 migration 脚本 flask db init # 4. 生成迁移脚本会对比 models.py 和当前数据库状态 flask db migrate -m init # 5. 执行迁移真正创建表结构 flask db upgrade执行完flask db upgrade后项目根目录下会出现notes.db文件且User表中已预置管理员账号见README.md用户名admin密码123456。若跳过第 3、4 步直接upgradeFlask-Migrate 会报错No such revision xxx—— 因为它找不到迁移历史记录。2.3 启动服务并验证flask run是开发模式但gunicorn才是答辩演示的底气开发阶段用flask run即可flask run --host0.0.0.0 --port5000访问http://localhost:5000你应该看到登录页。输入admin/123456进入 Dashboard新建笔记、添加标签、搜索内容全部可用。但答辩演示时必须用gunicorn启动项目已内置gunicorn.conf.py# 安装 gunicornrequirements.txt 已包含 pip install gunicorn # 启动监听 8000 端口4 个工作进程 gunicorn -c gunicorn.conf.py run:app为什么不用flask run演示flask run是 Werkzeug 开发服务器单线程、无并发、不处理静态文件缓存答辩时多人同时刷新页面极易触发500 Internal Server Error。而gunicorn是生产级 WSGI 服务器自带进程管理、请求队列、超时控制且gunicorn.conf.py中已配置timeout30、worker_classgevent提升 I/O 并发这才是让答辩老师信服“这系统真能用”的底层保障。3. Vue 前端与 Flask 后端如何协同看懂axios请求拦截器和cross_origin的真实作用3.1 前端请求逻辑所有 API 调用都经过request.js统一拦截Vue 前端代码位于app/static/编译后产物其核心是app/static/js/app.[hash].js中的请求封装。关键逻辑在request.js反编译可得// request.js简化版 import axios from axios const service axios.create({ baseURL: /api, // 注意所有请求路径以 /api 开头 timeout: 10000 }) // 请求拦截器自动携带 token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} // Bearer Token 认证 } return config }, error Promise.reject(error)) // 响应拦截器统一错误处理 service.interceptors.response.use(response { if (response.data.code ! 200) { // 后端约定code200 为成功 ElMessage.error(response.data.msg || 请求失败) return Promise.reject(new Error(response.data.msg)) } return response.data.data // 直接返回 data 字段省去 .data.data 嵌套 }, error { if (error.response?.status 401) { localStorage.removeItem(token) router.push(/login) // token 失效跳转登录页 } return Promise.reject(error) })参数说明baseURL: /api意味着前端发GET /notes实际请求的是http://localhost:5000/api/notesFlask 路由必须以/api开头见app/routes/api.py。Authorization: Bearer ${token}后端用flask_jwt_extended验证requirements.txt中已声明Flask-JWT-Extended4.4.4jwt_required()装饰器保护所有/api接口。response.data.data后端响应格式为{ code: 200, msg: success, data: { ... } }前端自动解包降低调用方心智负担。3.2 后端接口设计RESTful 不是口号是每个路由的命名逻辑app/routes/api.py中的接口严格遵循 REST 规范且全部返回 JSONHTTP 方法路径功能关键参数GET/api/notes获取笔记列表page1,per_page10,folder_id5,tag_id3POST/api/notes创建新笔记title,content,folder_id,tag_ids[1,2]数组GET/api/notes/int:id获取单个笔记无PUT/api/notes/int:id更新笔记title,content,is_pinned置顶DELETE/api/notes/int:id删除笔记无GET/api/folders获取文件夹树include_emptyfalse是否包含空文件夹注意所有接口都通过jwt_required()验证且POST/PUT请求体必须是application/jsonFlask-WTF表单只用于登录页API 层完全脱离表单验证。3.3 跨域问题不存在因为根本没跨域——Vue 是 Flask 的静态资源这是最容易被误解的一点项目没有跨域问题。很多同学看到axios发请求就条件反射配CORS但本项目中Vue 前端编译后放在app/static/下Flask 通过send_from_directory(app.static_folder, path)直接 serveindex.html和 JS/CSS所有axios请求的baseURL是/api即相对路径浏览器自动拼接为http://localhost:5000/api/xxx因此请求同源协议域名端口完全一致cross_origin装饰器完全不需要app/routes/api.py中未导入flask_cors。血泪经验曾有同学强行加flask_cors并设置origins*结果导致OPTIONS预检请求失败登录接口反复 401。删掉CORS相关代码问题立刻解决——因为根本没跨域加 CORS 反而引入额外复杂度。4. 高频踩坑与排查指南答辩前必过这五关否则可能当场重启服务器4.1 现象登录成功后跳转 Dashboard但页面空白控制台报Failed to load resource: the server responded with a status of 404 ()原因app/static/index.html中引用的 JS/CSS 路径错误。Vue CLI 默认生成相对路径如js/app.xxx.js但 Flask 的static_folder是app/static/所以实际路径应为/static/js/app.xxx.js。若index.html中写成js/app.xxx.js缺少/static/前缀Flask 找不到该文件。解决打开app/static/index.html将所有script srcjs/...和link hrefcss/...改为script src/static/js/...和link href/static/css/...。Vue CLI 构建时可通过vue.config.js的publicPath: /static/避免手动改。4.2 现象新建笔记后数据库notes表中content字段为空字符串但前端输入框有内容原因app/models.py中Note.content字段定义为db.Text()但前端提交的content是 HTML 字符串Vue 富文本编辑器输出而 SQLAlchemy 默认对Text字段做strip()处理若 HTML 开头有空格或换行会被清空。解决修改app/models.py在Note类中为content添加default和nullableFalse并移除Text()的隐式处理content db.Column(db.Text, nullableFalse, default)同时在app/routes/api.py的create_note函数中显式检查request.json.get(content)是否为空字符串而非None。4.3 现象搜索功能失效/api/notes/search?qxxx返回空数组但数据库中有匹配笔记原因SQLite 的LIKE查询默认区分大小写而app/routes/api.py中的搜索逻辑是Note.query.filter(Note.title.like(f%{q}%) | Note.content.like(f%{q}%)).all()若用户搜索python但笔记标题是Python则匹配失败。解决改用func.lower()统一转小写from sqlalchemy import func Note.query.filter( (func.lower(Note.title).like(f%{q.lower()}%)) | (func.lower(Note.content).like(f%{q.lower()}%)) ).all()4.4 现象部署到 Linux 服务器后flask db upgrade报错sqlite3.OperationalError: unable to open database file原因config.py中数据库路径写死为os.path.join(basedir, notes.db)但basedir是os.path.abspath(os.path.dirname(__file__))在gunicorn多进程下工作目录可能不是项目根目录导致notes.db被创建在/tmp或其他位置。解决在config.py中强制指定绝对路径import os basedir os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI fsqlite:///{os.path.join(basedir, .., notes.db)}并在gunicorn.conf.py中设置chdir /path/to/your/project确保工作目录正确。4.5 现象导出 PDF 功能点击无反应控制台报ReferenceError: window.jspdf is not defined原因PDF 导出使用jsPDF库但app/static/js/app.[hash].js中未正确引入。项目交付包中app/static/js/下缺少jspdf.umd.min.js文件可能是压缩时遗漏。解决下载 jsPDF 官方 UMD 版本 放入app/static/js/并在app/static/index.html的/body前添加script src/static/js/jspdf.umd.min.js/script然后在 Vue 组件中通过window.jspdf调用import jsPDF from jspdf在编译后环境不可用。5. 毕业答辩加分项三个可现场演示的进阶技巧让老师追问“你怎么想到的”5.1 技巧一用flask shell快速验证数据库操作比写 SQL 更直观答辩时老师常问“如果我要批量删除某标签下的所有笔记怎么操作” 别急着翻代码直接打开flask shellflask shell from app import db from app.models import Note, Tag # 查找 tag_namePython 的标签 python_tag Tag.query.filter_by(namePython).first() # 获取该标签下所有笔记注意是多对多关系需查关联表 notes Note.query.join(Note.tags).filter(Tag.id python_tag.id).all() len(notes) 37 # 批量删除注意先 delete 再 commit for note in notes: ... db.session.delete(note) ... db.session.commit() print(已删除 37 篇笔记)为什么比 SQL 强flask shell自动加载所有模型和数据库实例无需import对象关系映射ORM让关联查询一目了然避免手写JOIN的语法错误db.session.delete()是软删除标记为待删commit()才真正执行安全可控。5.2 技巧二用flask_profiler插件定位性能瓶颈现场展示“为什么搜索快”项目未内置性能分析但可一分钟接入flask-profilerpip install flask-profiler# 在 app/__init__.py 的 create_app() 函数末尾添加 from flask_profiler import Profiler profiler Profiler() profiler.init_app(app) # 启动时加 --with-profiler 参数 flask run --with-profiler启动后访问http://localhost:5000/flask-profiler能看到所有请求的耗时、SQL 查询次数、内存占用。例如搜索接口/api/notes/search的耗时从 120ms 降到 45ms是因为加了func.lower()后 SQLite 使用了索引需在title和content字段上建lower(title)函数索引flask db migrate生成新迁移脚本即可。5.3 技巧三用pytesttest_api.py实现接口自动化测试答辩 PPT 放测试覆盖率截图项目附带tests/test_api.py覆盖核心接口# tests/test_api.py def test_create_note(client, auth_header): response client.post(/api/notes, json{title: Test, content: Hello}, headersauth_header) assert response.status_code 201 assert response.json[title] Test def test_search_notes(client, auth_header): client.post(/api/notes, json{title: Python Guide}, headersauth_header) response client.get(/api/notes/search?qpython, headersauth_header) assert len(response.json) 1运行测试pytest tests/ --covapp --cov-reporthtml生成htmlcov/index.html打开后可见routes/api.py覆盖率 92%。答辩时展示这张图比说“我写了测试”有力十倍。从那以后我每次改完一个接口都强制走一遍pytest tests/test_api.py::test_xxx再git add。不是为了应付检查而是因为某次flask db migrate后忘了upgrade测试直接报no such table比等到答辩现场崩溃强一万倍。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站