表单保存成功、删除失败、登录密码错误------如果提示样式都一样,用户很难一眼分清有没有成功。
下面两件事可以一起做:
- 用 Flash 分类,让成功、失败、警告显示不同样式
- 做 404 等简单错误页,避免白屏或默认丑页面
例子仍用通用的 Note 备忘录,不涉及任何真实业务。
1. 学完后你能做什么
- 用
flash("消息", "分类")按类型提示 - 在模板里 只写一次,自动渲染不同样式
- 注册
@app.errorhandler(404)自定义找不到页面 - 理解 Flash 和 redirect 怎么配合
2. Flash 是什么
Flask 的 flash() 用来 存一条一次性消息:下次请求渲染页面时显示,显示完就消失。
from flask import flash, redirect, url_for
flash("保存成功")
return redirect(url_for("home.note_list"))
常见用法:操作完成后 redirect 到列表,在列表页统一弹出提示。
Flash 通常配合 redirect 使用。同一次请求里 flash 后直接 render_template 有时也能显示,但 「改数据 → redirect → 列表页出提示」 更稳妥,也能避免刷新重复提交。

3. 为什么要加「分类」
只有一句话时,模板只能写一种颜色:
<p style="color:green;">{{ msg }}</p>
项目一大,成功、失败、警告混在一起,不好维护。
Flask 支持 第二个参数当分类名:
flash("保存成功", "ok")
flash("标题不能为空", "err")
flash("该记录已被其他人修改", "warn")
模板里按分类套不同 CSS,会清晰很多。
4. 视图里怎么用
在 app/home/views.py 里统一约定分类名(团队内保持一致即可):
| 分类 | 含义 | 示例 |
|---|---|---|
ok |
成功 | 保存成功、已删除 |
err |
错误 | 校验失败、无权限 |
warn |
警告 | 部分失败、即将过期 |
@home.route("/notes/add/", methods="GET", "POST")
def note_add():
form = NoteForm()
if form.validate_on_submit():
note = Note(
title=form.title.data.strip(),
content=(form.content.data or "").strip(),
)
db.session.add(note)
db.session.commit()
flash("保存成功", "ok")
return redirect(url_for("home.note_list"))
if request.method == "POST":
flash("请检查表单填写是否正确", "err")
return render_template("home/note_form.html", form=form, title="新增备忘录")
删除示例:
@home.route("/notes/delete/<int:note_id>/", methods="POST")
def note_delete(note_id):
row = Note.query.get_or_404(note_id)
form = DeleteForm()
if form.validate_on_submit():
db.session.delete(row)
db.session.commit()
flash("已删除", "ok")
else:
flash("删除失败,请重试", "err")
return redirect(url_for("home.note_list"))
WTForms 字段级错误仍用 form.title.errors;整页级结果用 Flash 更合适。
5. 模板里统一渲染(放在 base 布局里)
若已用模板继承,把 Flash 区域放在 base.html,全站生效:
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul class="flash-list">
{% for category, msg in messages %}
<li class="flash-item flash-item--{{ category }}">{{ msg }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
关键是 with_categories=true,循环里才能同时拿到 category 和 msg。
对应 CSS(app/static/css/site.css):
.flash-list {
list-style: none;
padding: 0;
margin: 0 0 16px;
}
.flash-item {
padding: 10px 14px;
border-radius: 6px;
margin-bottom: 8px;
font-size: 14px;
}
.flash-item--ok {
background: #ecfdf5;
color: #047857;
border: 1px solid #a7f3d0;
}
.flash-item--err {
background: #fef2f2;
color: #b91c1c;
border: 1px solid #fecaca;
}
.flash-item--warn {
background: #fffbeb;
color: #b45309;
border: 1px solid #fde68a;
}
暗色主题项目里,把背景改成半透明即可,分类 class 名不必改。
6. 只显示某一类(可选)
某个页面只想看错误提示:
{% for msg in get_flashed_messages(category_filter='err') %}
<p class="flash-item flash-item--err">{{ msg }}</p>
{% endfor %}
登录页常见写法:只筛 err;成功登录直接跳转,不必在登录页 flash。
7. 自定义 404 页面
用户访问不存在的 /notes/99999/,或乱输 URL,默认 404 既不友好也不好看。
① 写模板 app/templates/errors/404.html
{% extends "base.html" %}
{% block title %}页面不存在{% endblock %}
{% block content %}
<h1>404</h1>
<p>你要找的页面不存在,可能已被删除或地址输错了。</p>
<p>
<a href="{{ url_for('home.note_list') }}">返回备忘录列表</a>
</p>
{% endblock %}
② 注册错误处理
在 app/__init__.py 或 app/home/views.py 里:
from flask import render_template
@app.errorhandler(404)
def page_not_found(e):
return render_template("errors/404.html"), 404
返回值要带 404 状态码,否则浏览器会认为请求成功。
若使用 Blueprint,也可以写在 Blueprint 上,或在 app 上统一注册------整站一个 404 页最常见。
③ 和 get_or_404 的关系
row = Note.query.get_or_404(note_id)
找不到记录时,Flask 会 抛出 404,走注册的 page_not_found,不必在每个视图里手写 if not row。
8. 500 错误页(了解即可)
服务器内部报错时,开发模式会看到调试栈;上线后应隐藏细节:
@app.errorhandler(500)
def internal_error(e):
db.session.rollback() # 若涉及数据库,避免脏 session
return render_template("errors/500.html"), 500
errors/500.html 写法与 404 类似,文案改成「服务暂时不可用,请稍后再试」即可。
9. 流程示意
POST 删除备忘录
│
▼
flash("已删除", "ok")
redirect → /notes/
│
▼
base.html 里 get_flashed_messages(with_categories=true)
│
▼
<li class="flash-item flash-item--ok">已删除</li>
访问 /notes/99999/
│
▼
get_or_404 → 404 异常
│
▼
@app.errorhandler(404) → errors/404.html
10. 新手常踩的 4 个坑
坑 1:忘了 with_categories=true
模板里拿不到分类,所有消息只能一种样式。
应写 get_flashed_messages(with_categories=true)。
坑 2:flash 后没有 redirect
POST 成功后直接 render_template,用户刷新可能 重复提交。
改数据后 redirect + flash 是更稳的习惯。
坑 3:404 处理函数忘了状态码
return render_template("errors/404.html") # 错:HTTP 200
return render_template("errors/404.html"), 404 # 对
坑 4:分类名不统一
有人写 success,有人写 ok,模板 CSS 会对不上。
项目里 定 3~4 个固定分类,写进约定或文档即可。
11. 实际项目里怎么用
- 成功 / 失败 / 警告 固定几种分类,Flash 区域 只在布局里写一次
- 增删改后 redirect 回列表 再出提示
- 404、500 继承同一套 base 布局,风格和正常页一致
- 生产环境关闭 debug,靠错误页给用户友好文案
flash("消息", "分类")--- 成功、失败、警告分开with_categories=true--- 模板里按分类渲染@app.errorhandler(404)--- 统一友好错误页- 改数据后 redirect --- 避免重复提交,Flash 更自然