Appearance
用户交互的核心是表单:一次完整交互 = 前端表单接收输入 + 后端处理 + 消息反馈给用户。本章掌握 GET/POST 基本交互与文件上传,用 Flask-WTF 把表单抽象成类(自带验证与 CSRF 保护),用闪现消息和自定义错误页做消息反馈,并集成 CKEditor 富文本编辑器。
GET 与 POST 请求
| 维度 | GET | POST |
|---|---|---|
| 用途 | 从服务器获取数据 | 向服务器提交数据 |
| 数据传递 | URL 查询字符串(?param=value) | 请求体,不显示在 URL |
| 安全性 | 明文可见(历史记录/日志) | 相对更安全,适合敏感信息 |
| 缓存 | 可被浏览器缓存 | 不缓存 |
| 幂等性 | 多次请求无副作用 | 多次请求可能改变服务器状态 |
基本交互与文件上传
接收 GET/POST 数据
python
from flask import request
@app.route('/form', methods=['GET', 'POST'])
def form():
msg_get = request.args.get('msg_get') # GET 数据
msg_post = request.form.get('msg_post') # POST 数据
return render_template('form.html', msg_get=msg_get, msg_post=msg_post)文件上传(两个视图函数)
python
import os
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['UPLOADED_PATH'] = os.path.join(basedir, 'uploads')
os.makedirs(app.config['UPLOADED_PATH'], exist_ok=True)
# ① 获取已上传的文件
@app.route('/files/<filename>')
def uploaded_files(filename):
return send_from_directory(app.config['UPLOADED_PATH'], filename)
# ② 接收上传
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
f = request.files.get('upload')
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
files = os.listdir(app.config['UPLOADED_PATH'])
return render_template('upload.html', files=files)上传表单必须设置
enctype="multipart/form-data"。
Flask-WTF 表单
Flask-WTF 把 WTForms 集成进 Flask:将表单抽象为类,降低维护难度;自带数据验证与 CSRF 保护。
bash
pip install flask-wtf==0.14.21. 定义表单类
所有表单类继承 FlaskForm,字段用类变量描述:
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField(label='用户名')
password = PasswordField(label='密码')
submit = SubmitField(label='登录')使用前必须配置密钥(CSRF 需要):
python
app.config['SECRET_KEY'] = 'Chapter4' # 生产环境用随机字符串2. 视图函数
python
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST' and form.validate_on_submit():
return 'Hello %s! 密码:%s' % (form.username.data, form.password.data)
return render_template('login.html', form=form)3. 渲染表单(三种方式)
html
{% extends 'bootstrap/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
<!-- 方式1:手动逐个渲染(需含 csrf_token) -->
<form method="post">{{ form.username }}{{ form.password }}{{ form.csrf_token }}{{ form.submit }}</form>
<!-- 方式2:form_field 宏逐个美化 -->
{{ wtf.form_field(form.username) }}
<!-- 方式3:quick_form 快速渲染(推荐,代码最少) -->
{{ wtf.quick_form(form) }}常用字段类型
| 字段 | 说明 |
|---|---|
StringField / TextAreaField | 文本 / 多行文本 |
PasswordField | 密码框 |
HiddenField | 隐藏字段 |
DateField / DateTimeField | 日期 / 日期时间 |
IntegerField / FloatField | 整数 / 浮点数 |
BooleanField / RadioField | 复选框 / 单选 |
SelectField / SelectMultipleField | 下拉 / 多选下拉 |
FileField | 文件上传 |
SubmitField | 提交按钮 |
常用验证器
| 验证器 | 说明 |
|---|---|
DataRequired | 必填,数据不为空 |
Email | 验证邮箱格式 |
EqualTo | 比较两字段(二次密码确认) |
Length | 校验字符串长度 |
NumberRange | 数值范围 |
IPAddress / Regexp | IP 地址 / 正则匹配 |
Optional | 选填,为空时跳过其他验证 |
在字段中添加 validators=[...](list 类型)即可启用多个验证器;复杂表单用 form.data 获取 dict 形式全部数据。
消息反馈
闪现消息(flash)
后端 flash('内容', '类别') 发送,模板用 get_flashed_messages(with_categories=true) 输出;Flask-Bootstrap 提供现成宏:
python
from flask import flash
flash('这是一条测试消息', 'success') # 类别: success/danger/info/warninghtml
<!-- 手动遍历 -->
{% for category, message in get_flashed_messages(with_categories=true) %}
<h5>[{{ category }}] - {{ message }}</h5>
{% endfor %}
<!-- 或直接用 Bootstrap 宏 -->
{% import 'bootstrap/utils.html' as utils %}
{{ utils.flashed_messages() }}自定义错误页
用 app.errorhandler(状态码) 绑定,替代默认纯文字错误页:
python
from flask import abort, request
@app.errorhandler(404)
def not_found_page(e):
return render_template('custom_error.html', title='Emmmm, 404!',
description='搞错url了吧:%s' % request.path), 404
# 在视图函数中主动触发:abort(404) / abort(500)500 页调试时可显示异常(traceback.print_exc),上线后必须隐藏错误详情,改用日志记录。
Flask-CKEditor 富文本
CKEditor 让用户在浏览器中编辑富文本(格式化、插图、表格)。
bash
pip install flask-ckeditor==0.4.3python
from flask_ckeditor import CKEditor, CKEditorField
app.config['CKEDITOR_SERVE_LOCAL'] = True # 用本地资源
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload_ckeditor' # 指定上传视图
ckeditor = CKEditor()
ckeditor.init_app(app)
class ArticleForm(FlaskForm):
title = StringField(label='标题', validators=[DataRequired()])
content = CKEditorField(label='内容', validators=[DataRequired()])
submit = SubmitField(label='查看')模板中额外加载与配置:
html
{{ ckeditor.load() }}
{{ ckeditor.config(name='content', height=320) }}查看页输出富文本内容时须用 | safe 关闭 HTML 转义(数据为 HTML 格式):
html
{{ form.content.data | safe }}上传图片:视图函数校验扩展名后用 upload_success(url=...) / upload_fail(message=...) 返回 CKEditor 要求的格式。
实践建议
- 表单一律用 Flask-WTF +
quick_form,不要手写 HTML 表单——代码量减少、自带验证与 CSRF SECRET_KEY生产环境必须用随机字符串,不能硬编码- 操作结果用 flash 反馈,错误页面用
errorhandler定制保持风格统一 - 富文本内容输出务必用
| safe(前提:内容来源可信),否则有 XSS 风险
一句话总结
别再手写 HTML 表单——用 Flask-WTF 把表单写成类(自带验证与 CSRF,需配 SECRET_KEY),渲染首选 wtf.quick_form(form);操作结果用 flash 反馈,富文本输出必须 | safe(否则有 XSS 风险)。