Flask 笔记八:Flash 分类提示与简单错误页

表单保存成功、删除失败、登录密码错误------如果提示样式都一样,用户很难一眼分清有没有成功。

下面两件事可以一起做:

  1. 用 Flash 分类,让成功、失败、警告显示不同样式
  2. 做 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,循环里才能同时拿到 categorymsg

对应 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__.pyapp/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,靠错误页给用户友好文案

  1. flash("消息", "分类") --- 成功、失败、警告分开
  2. with_categories=true --- 模板里按分类渲染
  3. @app.errorhandler(404) --- 统一友好错误页
  4. 改数据后 redirect --- 避免重复提交,Flash 更自然