news 2026/9/13 3:10:28

Flask入门教程(十二):表单处理——从基础到Flask-WTF完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flask入门教程(十二):表单处理——从基础到Flask-WTF完整实践

1. 基本表单处理

Flask提供了直接处理表单数据的方式,使用request对象来获取提交的数据。

创建HTML表单

templates/form.html:

<!DOCTYPE html> <html> <head> <title>Form Example</title> </head> <body> <form action="/submit" method="post"> <label for="name">Name:</label> <input type="text" id="name" name="name"> <br> <label for="email">Email:</label> <input type="email" id="email" name="email"> <br> <input type="submit" value="Submit"> </form> </body> </html>
  • action="/submit":表单数据提交到/submit路径

  • method="post":使用POST方法提交数据(敏感数据不应使用GET)

处理表单数据

app.py:

from flask import Flask, render_template, request app = Flask(__name__) @app.route('/') def form(): return render_template('form.html') @app.route('/submit', methods=['POST']) def submit(): name = request.form.get('name') email = request.form.get('email') return f'Name: {name}, Email: {email}' if __name__ == '__main__': app.run(debug=True)

2. 使用Flask-WTF扩展

Flask-WTF是一个封装了WTForms的扩展,提供了表单处理和验证的功能,使得表单处理更加简洁和强大。

安装Flask-WTF

pip install flask-wtf

配置与定义表单

app.py:

from flask import Flask, render_template, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField, EmailField, SubmitField from wtforms.validators import DataRequired, Email app = Flask(__name__) app.secret_key = 'your_secret_key' # CSRF保护需要密钥 class MyForm(FlaskForm): name = StringField('Name', validators=[DataRequired()]) email = EmailField('Email', validators=[DataRequired(), Email()]) submit = SubmitField('Submit') @app.route('/', methods=['GET', 'POST']) def form(): form = MyForm() if form.validate_on_submit(): name = form.name.data email = form.email.data return f'Name: {name}, Email: {email}' return render_template('form.html', form=form)

模板中渲染WTForms

templates/form.html:

<!DOCTYPE html> <html> <head> <title>Form Example</title> </head> <body> <form method="post"> {{ form.hidden_tag() }} <div> {{ form.name.label }}<br> {{ form.name(size=32) }} </div> <div> {{ form.email.label }}<br> {{ form.email(size=32) }} </div> <div> {{ form.submit() }} </div> </form> </body> </html>
模板语法作用
{{ form.hidden_tag() }}生成CSRF令牌隐藏字段
{{ form.name.label }}渲染字段的标签
{{ form.name(size=32) }}渲染字段控件,可传入HTML属性
{{ form.submit() }}渲染提交按钮

3. 常用字段类型

WTForms提供了丰富的字段类型,覆盖了绝大多数表单场景:

字段类说明HTML渲染
StringField文本输入<input type="text">
EmailField邮箱输入<input type="email">
PasswordField密码输入<input type="password">
IntegerField整数输入<input type="number">
FloatField浮点数输入<input type="number" step="any">
BooleanField复选框<input type="checkbox">
SelectField下拉选择<select>
RadioField单选按钮<input type="radio">
TextAreaField多行文本<textarea>
SubmitField提交按钮<input type="submit">
FileField文件上传<input type="file">
DateField日期输入<input type="date">
DateTimeField日期时间输入<input type="datetime-local">

完整表单示例

from wtforms import StringField, PasswordField, BooleanField, SelectField, TextAreaField, SubmitField from wtforms.validators import DataRequired, Email, Length, EqualTo class RegisterForm(FlaskForm): username = StringField('用户名', validators=[ DataRequired(), Length(min=3, max=20, message="用户名长度必须在3-20个字符之间") ]) email = EmailField('邮箱', validators=[DataRequired(), Email()]) password = PasswordField('密码', validators=[ DataRequired(), Length(min=6, message="密码至少6个字符") ]) confirm = PasswordField('确认密码', validators=[ DataRequired(), EqualTo('password', message="两次密码输入不一致") ]) gender = SelectField('性别', choices=[('male', '男'), ('female', '女'), ('other', '其他')]) bio = TextAreaField('个人简介') agree = BooleanField('同意用户协议', validators=[DataRequired()]) submit = SubmitField('注册')

4. 表单验证器

Flask-WTF和WTForms提供了丰富的表单验证功能。

内置验证器

验证器作用
DataRequired()确保字段不为空
Email()验证是否为有效的邮箱地址
Length(min, max)限制字符串长度范围
EqualTo(fieldname)验证是否与另一个字段值相同
NumberRange(min, max)限制数值范围
URL()验证是否为有效的URL
Regexp(regex)使用正则表达式验证
IPAddress()验证是否为有效的IP地址
UUID()验证是否为有效的UUID
AnyOf(values)验证值是否在指定列表中
NoneOf(values)验证值不在指定列表中

自定义验证器

除了内置验证器,还可以在表单类中定义validate_字段名方法实现自定义验证:

class RegisterForm(FlaskForm): username = StringField('用户名', validators=[DataRequired()]) email = EmailField('邮箱', validators=[DataRequired(), Email()]) submit = SubmitField('注册') # 自定义验证:检查用户名是否包含敏感词 def validate_username(self, field): if 'admin' in field.data.lower(): raise ValidationError('用户名不能包含 "admin"') # 自定义验证:检查邮箱是否已被注册(实际场景中需查询数据库) def validate_email(self, field): if field.data == 'test@example.com': raise ValidationError('该邮箱已被注册')

5. 显示验证错误

在模板中显示字段的验证错误:

<form method="post"> {{ form.hidden_tag() }} <div> {{ form.username.label }}<br> {{ form.username(size=32) }} {% if form.username.errors %} <ul style="color:red;"> {% for error in form.username.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} </div> <div> {{ form.email.label }}<br> {{ form.email(size=32) }} {% if form.email.errors %} <ul style="color:red;"> {% for error in form.email.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} </div> <div> {{ form.submit() }} </div> </form>

6. 文件上传

Flask支持处理文件上传,文件通过request.files访问。

创建文件上传表单

templates/upload.html:

<!DOCTYPE html> <html> <head> <title>Upload File</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <label for="file">File:</label> <input type="file" id="file" name="file"> <br> <input type="submit" value="Upload"> </form> </body> </html>

enctype="multipart/form-data"是文件上传必需的编码类型。

使用Flask-WTF的FileField

import os from flask import Flask, render_template, request from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileRequired, FileAllowed from wtforms import SubmitField from werkzeug.utils import secure_filename app = Flask(__name__) app.secret_key = 'secret' app.config['UPLOAD_FOLDER'] = 'uploads/' class UploadForm(FlaskForm): file = FileField('选择文件', validators=[ FileRequired('请选择一个文件'), FileAllowed(['jpg', 'png', 'pdf', 'txt'], '只允许上传 jpg, png, pdf, txt 文件') ]) submit = SubmitField('上传') @app.route('/upload', methods=['GET', 'POST']) def upload(): form = UploadForm() if form.validate_on_submit(): file = form.file.data filename = secure_filename(file.filename) os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return f'文件 {filename} 上传成功!' return render_template('upload.html', form=form)

文件上传验证器

验证器作用
FileRequired()确保已选择文件
FileAllowed(['jpg', 'png'])限制允许的文件扩展名

7. CSRF保护

Flask-WTF自动为表单提供CSRF(跨站请求伪造)保护。

启用CSRF保护

app.secret_key = 'your_secret_key' # CSRF令牌加密需要

模板中添加CSRF令牌

<form method="post"> {{ form.hidden_tag() }} <!-- 其他表单字段 --> </form>

form.hidden_tag()会生成一个隐藏的CSRF令牌字段,提交时Flask-WTF会自动验证。

全局CSRF保护

也可以为所有视图启用CSRF保护,包括非WTForms表单:

from flask_wtf.csrf import CSRFProtect csrf = CSRFProtect(app) # 或在应用工厂模式中 # csrf = CSRFProtect() # csrf.init_app(app)

对于AJAX请求,需要将CSRF令牌放在请求头中:

// 在模板中获取CSRF令牌 const csrfToken = "{{ csrf_token() }}"; fetch('/api/data', { method: 'POST', headers: { 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json' }, body: JSON.stringify(data) });

8. 表单处理完整流程对照

步骤纯Flask手动方式Flask-WTF方式
1. 定义表单写HTML模板Python表单类
2. 渲染表单手写HTML{{ form.field }}
3. 获取数据request.form.get('name')form.name.data
4. 验证数据手写if判断form.validate_on_submit()
5. 错误处理手动处理自动收集到form.field.errors
6. CSRF保护需手动实现自动处理(hidden_tag()

小结

本章全面讲解了Flask的表单处理。基础方式使用request.form手动获取和验证数据,适合简单场景;Flask-WTF扩展提供了更完善的方案——通过表单类定义字段和验证器,validate_on_submit()自动完成验证,模板中form.hidden_tag()自动生成CSRF令牌。内置验证器涵盖DataRequiredEmailLengthEqualTo等常见需求,也支持自定义验证方法。文件上传使用FileField配合FileRequiredFileAllowed验证器,配合secure_filename确保安全。Flask-WTF是Flask表单处理的标准方案,强烈推荐在实际项目中使用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 3:10:17

MySQL 中的窗口函数

窗口函数是 MySQL 8.0 引入的一个重要特性&#xff0c;它可以在不改变数据行数的前提下&#xff0c;对每一行数据进行聚合、排名、累积等计算。与 GROUP BY 不同&#xff0c;窗口函数会保留所有原始行。一、窗口函数窗口函数的执行逻辑是&#xff1a;在每一行数据上&#xff0c…

作者头像 李华
网站建设 2026/9/13 3:10:27

GLM付费首日DeepSeek重夺榜首,编程模型选型对比指南

GLM 付费首日&#xff0c;DeepSeek 重夺榜首。这个标题看起来像一场榜单排名的短期波动&#xff0c;但对经常折腾本地模型、API 接入和编码工具的开发者来说&#xff0c;它其实是两条产品路线之间的一次正面碰撞&#xff1a;一边是智谱 GLM 在编程场景快速发力&#xff0c;用 C…

作者头像 李华
网站建设 2026/9/13 3:08:43

Vibe Coding实战:一周5个项目烧掉100亿Token的经验总结

最近一段时间&#xff0c;Vibe Coding 几乎成了 AI 编程圈最热门的关键词。身边有朋友用它半天搓出一个工具站&#xff0c;也有团队拿它重写内部系统&#xff0c;但更多人是“烧了几百万 token 才发现代码根本没法上线”。我集中用 Vibe Coding 的方式做了一周实验&#xff0c;…

作者头像 李华
网站建设 2026/9/13 3:10:18

视频编解码算法工程师笔试核心解析:从率失真到RK3588硬件编解码

做了这么多年音视频技术&#xff0c;陆陆续续帮不少人复盘过各种厂子的编解码笔试。一个特别强烈的感受是&#xff1a;视频编解码算法工程师的笔试&#xff0c;和市面上绝大多数“算法工程师”岗位的笔试根本不在一个频道上。别人在刷LeetCode、追Transformer&#xff0c;你却在…

作者头像 李华
网站建设 2026/9/1 10:18:49

高频必考!滑动窗口最大值:单调队列如何把 O(nk) 优化到 O(n)?

LeetCode 239「滑动窗口最大值」&#xff0c;是Hard难度的经典题&#xff0c;也是各大厂面试的高频题。 给你一个数组和窗口大小k&#xff0c;窗口每滑一步&#xff0c;就要立刻知道窗口内的最大值。 暴力&#xff1a;每个窗口遍历一遍 → O(nk)&#xff0c;n1e5 时直接炸大顶堆…

作者头像 李华
网站建设 2026/9/3 20:42:18

Grok 4.6 登陆 Azure AI Foundry:企业级模型部署与调用实战

当一条“Grok 4.6 登陆微软 Foundry 平台”的消息出现在信息流里&#xff0c;多数开发者的第一反应是&#xff1a;又多了一个模型入口。但如果你正在负责团队的 AI 基础设施选型&#xff0c;看到这条消息的感受会完全不同——这意味着你可以在企业已经使用的 Azure 生态里&…

作者头像 李华