Flask 笔记十:把查询逻辑抽到 service,让 views 变薄
上一篇我们做了登录、Session 和login_required。路由能保护了但views.py往往还会越来越长读参数、拼 SQL、分页、再render_template全挤在一个函数里。这一篇做一件事把「怎么查数据」从视图里挪出去视图只负责「读请求 → 调函数 → 选模板」。例子仍是通用的Note备忘录不涉及任何真实业务。1. 学完后你能做什么分清 视图该写什么、service 该写什么新建note_service.py把列表查询抽成函数登录后 「只看自己的备忘录」 也放在 service 里同一个查询函数列表页和导出 可以共用知道什么时候 不必再抽一层2. 视图变胖通常长什么样第五篇你可能已经写过类似代码home.route(/notes/)login_requireddef note_list():q (request.args.get(q) or ).strip()page request.args.get(page, 1, typeint)user_id session.get(user_id)query Note.query.filter_by(user_iduser_id).order_by(Note.addtime.desc())if q:like f%{q}%query query.filter(or_(Note.title.like(like), Note.content.like(like)))page_data query.paginate(pagepage, per_page10)return render_template(home/note_list.html,page_datapage_data,delete_formDeleteForm(),qq,)能跑。但再加「日期筛选」「置顶优先」「admin 后台也要同逻辑」时这段查询会 复制粘贴好几份改一处漏一处。问题不在 SQLAlchemy而在 职责混在一起层次该关心什么视图 viewsHTTP读参数、鉴权、redirect、选模板service业务查询过滤谁的数据、拼条件、分页模板展示3. 先建app/note_service.py新建文件专门放和Note有关的查询from sqlalchemy import or_from app.models import Notedef list_notes_for_user(user_id: int,*,q: str ,date_from: str ,date_to: str ,page: int 1,per_page: int 10,):某用户的备忘录列表支持搜索、日期、分页。query (Note.query.filter_by(user_iduser_id).order_by(Note.addtime.desc()))q (q or ).strip()if q:like f%{q}%query query.filter(or_(Note.title.like(like), Note.content.like(like)))date_from (date_from or ).strip()date_to (date_to or ).strip()if date_from:query query.filter(Note.addtime date_from)if date_to:query query.filter(Note.addtime date_to 23:59:59)return query.paginate(pagepage, per_pageper_page)几个习惯关键字参数*, q...调用时一眼能看出传了什么返回数据这里是page_data不render_template函数名说清用途list_notes_for_user不是含糊的get_notes4. 视图变薄app/home/views.pyfrom flask import request, session, render_templatefrom app.auth_utils import login_requiredfrom app.forms import DeleteFormfrom app.note_service import list_notes_for_userhome.route(/notes/)login_requireddef note_list():q (request.args.get(q) or ).strip()date_from (request.args.get(date_from) or ).strip()date_to (request.args.get(date_to) or ).strip()page request.args.get(page, 1, typeint)page_data list_notes_for_user(session[user_id],qq,date_fromdate_from,date_todate_to,pagepage,)return render_template(home/note_list.html,page_datapage_data,delete_formDeleteForm(),qq,date_fromdate_from,date_todate_to,)对比之前中间一大段 SQL 没了读起来像目录——先读参数再调 service再渲染。登录保护仍放在 视图 装饰器service 假定「调用方已经知道 user_id」不读session后面会说为什么。5. 单条查询也抽出来编辑、删除前都要「按 id 取一条且必须是本人的」def get_note_for_user(note_id: int, user_id: int):取一条备忘录不存在或不属于该用户则返回 None。return Note.query.filter_by(idnote_id, user_iduser_id).first()编辑视图home.route(/notes/edit/int:note_id/, methods[GET, POST])login_requireddef note_edit(note_id):user_id session[user_id]row get_note_for_user(note_id, user_id)if not row:flash(记录不存在或无权访问, err)return redirect(url_for(home.note_list))form NoteForm()if request.method GET:form.title.data row.titleform.content.data row.contentif form.validate_on_submit():row.title form.title.data.strip()row.content (form.content.data or ).strip()db.session.commit()flash(保存成功, ok)return redirect(url_for(home.note_list))return render_template(home/note_form.html, formform, title编辑备忘录)比Note.query.get_or_404(note_id)更安全别人的 id 不会误改直接当「没有」处理。6. service 为什么不读 session新手常写def list_notes_for_user(...):user_id session.get(user_id) # 不推荐短期省事长期麻烦批处理脚本、定时任务没有 HTTP 请求没有 session单元测试要 mock session同一个函数不好区分「查 A 用户」还是「查 B 用户」更好做法谁调用谁传user_id。视图从 session 取脚本从参数取service 只认数字 id。鉴权有没有登录留在 装饰器 / 视图数据归属这条是不是你的放在 service 或视图里显式传 user_id。7. 一个查询多处复用以后若要 导出 CSV不必复制 SQLfrom app.note_service import list_notes_for_userdef export_my_notes_csv(user_id):# 不分页取全量per_page 设大或另写 list_notes_for_user_allpage_data list_notes_for_user(user_id, per_page10000)rows page_data.items# 写 CSV ...列表页、导出、后台统计共用同一套过滤规则改搜索逻辑只改 service 一处。8. 文件怎么摆入门够用不必搞复杂目录小项目常见app/├── home/│ └── views.py # 前台路由├── admin/│ └── views.py # 后台路由下一篇可拆 Blueprint├── models.py├── forms.py├── auth_utils.py # login_required├── note_service.py # Note 相关查询└── user_service.py # User 相关可选命名习惯xxx_service.py或xxx_queries.py都行团队统一即可。一个文件对应一块业务别把所有表的查询塞进一个service.py几千行。9. 流程示意GET /notes/?q会议│▼login_required 确认已登录│▼views.note_list 读 request.args、session[user_id]│▼list_notes_for_user(user_id, q会议, ...)│▼返回 paginate 结果不碰模板│▼render_template(note_list.html, page_data...)GET /notes/edit/99/别人的 id│▼get_note_for_user(99, my_user_id) → None│▼flash redirect不暴露「有这条但你看不见」10. 新手常踩的 5 个坑坑 1service 里render_templateservice 应 返回数据渲染是视图的事。混在一起以后没法给 JSON API 复用。坑 2service 里flash/redirect同上属于 HTTP 层。service 返回None或抛自定义异常由视图决定怎么提示用户。坑 3过度抽象只有一条Note.query.get(id)不必再包三层。 重复第二次时再抽。坑 4忘记在 service 里过滤user_id登录只保证「你是谁」不保证「你能动别人的数据」。写操作、按 id 查单条都要带 user_id。坑 5service 之间循环 importnote_service调user_serviceuser_service又调note_service会炸。共用小逻辑可放models或utils.py大模块之间尽量 单向依赖。11. 和「大项目」的关系真实项目里常见分层名字更多Repository、DAO、Domain但入门阶段记住一条就够视图处理 Webservice 处理「查什么、怎么查」。你项目里若看到load_novel_chapter_view()、search_notes()这类函数套路相同视图短、查询集中、名字说清楚用途。不必急着学 Application Factory 或复杂架构先把重复的 SQL 从 views 挪出去收益已经很大。12. 小结记住四件事views — 读参数、鉴权、redirect、render_templateservice — 拼查询、分页、返回数据不读 sessionuser_id显式传入 — 列表、单条查询都要管数据归属重复再抽 — 别为单行查询建十层抽象

相关新闻

过拟合的本质与六大实操防御方案

过拟合的本质与六大实操防御方案

1. 这不是模型“学得太好”,而是它在考试前偷偷背了答案“过拟合”这个词,刚接触机器学习的人常会误解成“模型太强了”“训练得特别充分”。我带过不少转行做数据科学的同事,头两周几乎都踩过这个坑——他们看着训练集上99.6%的准确率喜出望…

2026/6/25 19:15:58阅读更多 →
融合CV与密码学:构建自适应GUI自动化测试新范式

融合CV与密码学:构建自适应GUI自动化测试新范式

1. 项目概述:当GUI测试遇见CV与密码学做自动化测试的朋友,尤其是搞UI/GUI自动化的,这几年估计都挺头疼。传统的基于元素定位的框架,像Selenium、Appium,在应对现代复杂、动态、甚至带点“花活”的界面时,越…

2026/6/25 19:15:58阅读更多 →
MAML++实战指南:元学习小样本泛化稳定性优化

MAML++实战指南:元学习小样本泛化稳定性优化

1. 项目概述:从MAML到MAML,元学习优化路径的实战拆解“From MAML to MAML”这个标题乍看像一篇综述论文的副标题,但对真正跑过元学习实验的人来说,它背后是一条踩满坑、调烂超参、反复重训模型的实操路线图。我从2019年第一次用Py…

2026/6/25 19:15:58阅读更多 →
Claude Managed Agents:智能体运行时的基础设施革命

Claude Managed Agents:智能体运行时的基础设施革命

1. 这不是新赛道,而是基础设施层的“操作系统时刻”你打开终端,敲下docker run -it ubuntu:24.04 /bin/bash,几秒后就拥有了一个隔离、可复现、带完整文件系统的 Linux 环境——这件事在今天稀松平常。但回到 2008 年,这需要你手动…

2026/6/25 20:26:23阅读更多 →
ELK收集网络设备日志

ELK收集网络设备日志

分步走:1、新建一台Linux虚拟机做为rsyslog服务器接收网络设备日志2、在上述虚拟机上部署filebeat,将接收到的网络日志发送到logstash上3、最后通过logstash针对性配置将日志发送给es并在kibana上显示。1、部署rsyslog服务器1、新建rsyslog配置文件 [roo…

2026/6/25 20:26:23阅读更多 →
多重共线性诊断与处理:VIF、条件指数与业务驱动的特征重构

多重共线性诊断与处理:VIF、条件指数与业务驱动的特征重构

1. 这不是“模型跑不起来”的锅,而是你没看清变量之间的悄悄话我带过二十多个从零起步的数据分析项目,也帮十多家公司做过模型诊断。每次遇到“特征重要性全飘忽不定”“系数符号反直觉”“p值忽大忽小像抽风”这类问题,八成以上不是算法选错…

2026/6/25 20:26:23阅读更多 →
LangChain 文本分割器完全指南:从原理到实战选择

LangChain 文本分割器完全指南:从原理到实战选择

如果说 RAG(检索增强生成)是现代 AI 应用的基础架构,那么文本分割就是这个基础架构的地基。选对分割器,你的向量检索精度能提升 30%;选错了,再好的模型也救不了你的 RAG 系统。为什么要关注文本分割&#x…

2026/6/25 20:26:23阅读更多 →
【课程设计/毕业设计】基于 Django 的网络设备分时租赁管理系统设计与实现 基于 Django 的一体化网络设备租赁管控系统设计与实现【附源码、数据库、万字文档】

【课程设计/毕业设计】基于 Django 的网络设备分时租赁管理系统设计与实现 基于 Django 的一体化网络设备租赁管控系统设计与实现【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/6/25 20:26:23阅读更多 →
抖音自动上传工具终极指南:3步实现视频发布完全自动化

抖音自动上传工具终极指南:3步实现视频发布完全自动化

抖音自动上传工具终极指南:3步实现视频发布完全自动化 【免费下载链接】douyin_uplod 抖音自动上传发布视频 项目地址: https://gitcode.com/gh_mirrors/do/douyin_uplod 还在为每天重复上传抖音视频而烦恼吗?这款开源的抖音自动上传工具将彻底改…

2026/6/25 20:21:18阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/25 9:39:54阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/25 2:52:24阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/25 9:01:34阅读更多 →
面试辅助工具横评:我试了5款AI面试工具,最后留下了OfferGo

面试辅助工具横评:我试了5款AI面试工具,最后留下了OfferGo

上半年跳槽,面了十几家公司。说句实话,不是能力不行,是面试现场太容易崩了。 明明准备了一周,面试官换个问法脑子就一片白。面完之后那个懊悔——其实我会的。 后来开始试市面上的AI面试辅助工具。前前后后装了5款,踩…

2026/6/25 11:52:11阅读更多 →
Claude Code 提示词设计:从塑造“人格”到建立“状态机”

Claude Code 提示词设计:从塑造“人格”到建立“状态机”

当前 AI Agent 设计的核心痛点在于:大模型不缺写代码的能力,缺的是克制力、边界感和验证逻辑。Prompt 不再是用来塑造“人格”的,而是用来建立“状态机(State Machine)”和“行为门禁(Guardrails&#xff0…

2026/6/25 11:52:11阅读更多 →
MC-037 | 自定义 Skill 开发:创建你的AI能力模块

MC-037 | 自定义 Skill 开发:创建你的AI能力模块

MONKEYCODE 教程系列 MonkeyCode教程及推广系列 MC-037 自定义 Skill 开发:创建你的AI能力模块 >官网链接注册更放心哦https://monkeycode-ai.com/?ic019e0aed-c823-783c-b08a-4f030f891e4e 系列: 不爱土豆唯爱马铃薯 MonkeyCode 教程系列 字数: 约 1400 字…

2026/6/25 11:52:11阅读更多 →