Flask - WTF扩展 #
一、Flask-WTF概述 #
1.1 什么是Flask-WTF #
Flask-WTF是Flask的表单处理扩展,集成了WTForms库,提供了表单验证、CSRF保护等功能。
1.2 安装 #
bash
pip install flask-wtf
1.3 配置 #
python
from flask import Flask
from flask_wtf import FlaskForm
app = Flask(__name__)
# 必须设置SECRET_KEY用于CSRF保护
app.config['SECRET_KEY'] = 'your-secret-key'
二、创建表单类 #
2.1 基本表单 #
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired()])
submit = SubmitField('登录')
2.2 在视图中使用 #
python
from flask import render_template, redirect, url_for
from forms import LoginForm
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 表单验证通过
username = form.username.data
password = form.password.data
# 处理登录逻辑
return redirect(url_for('index'))
return render_template('login.html', form=form)
三、字段类型 #
3.1 基本字段 #
python
from wtforms import (
StringField, # 文本输入
PasswordField, # 密码输入
TextAreaField, # 多行文本
HiddenField, # 隐藏字段
IntegerField, # 整数
FloatField, # 浮点数
DecimalField, # 十进制数
BooleanField, # 复选框
DateField, # 日期
DateTimeField, # 日期时间
TimeField, # 时间
EmailField, # 邮箱
TelField, # 电话
URLField, # URL
SearchField, # 搜索框
FileField, # 文件上传
MultipleFileField,# 多文件上传
SelectField, # 下拉选择
SelectMultipleField, # 多选下拉
RadioField, # 单选框
SubmitField, # 提交按钮
)
3.2 字段示例 #
python
class ProfileForm(FlaskForm):
# 文本字段
username = StringField('用户名')
bio = TextAreaField('个人简介')
# 密码字段
password = PasswordField('密码')
# 数字字段
age = IntegerField('年龄')
price = FloatField('价格')
# 日期时间字段
birthday = DateField('生日', format='%Y-%m-%d')
created_at = DateTimeField('创建时间')
# 邮箱和URL
email = EmailField('邮箱')
website = URLField('网站')
# 布尔字段
active = BooleanField('激活')
# 选择字段
country = SelectField('国家', choices=[
('cn', '中国'),
('us', '美国'),
('uk', '英国')
])
# 单选字段
gender = RadioField('性别', choices=[
('male', '男'),
('female', '女')
])
# 文件字段
avatar = FileField('头像')
# 提交按钮
submit = SubmitField('保存')
3.3 字段参数 #
python
username = StringField(
'用户名', # 标签
validators=[DataRequired()], # 验证器
default='', # 默认值
description='请输入用户名', # 描述
render_kw={ # HTML属性
'class': 'form-control',
'placeholder': '用户名',
'required': True
}
)
四、验证器 #
4.1 内置验证器 #
python
from wtforms.validators import (
DataRequired, # 必填
Email, # 邮箱格式
Length, # 长度限制
NumberRange, # 数值范围
URL, # URL格式
EqualTo, # 等于另一个字段
Regexp, # 正则表达式
Optional, # 可选
InputRequired, # 输入必填
AnyOf, # 必须是指定值之一
NoneOf, # 不能是指定值
)
4.2 验证器示例 #
python
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(message='用户名不能为空'),
Length(min=3, max=20, message='用户名长度3-20个字符')
])
email = StringField('邮箱', validators=[
DataRequired(message='邮箱不能为空'),
Email(message='邮箱格式不正确')
])
password = PasswordField('密码', validators=[
DataRequired(message='密码不能为空'),
Length(min=6, message='密码至少6个字符')
])
confirm = PasswordField('确认密码', validators=[
DataRequired(message='请确认密码'),
EqualTo('password', message='两次密码不一致')
])
age = IntegerField('年龄', validators=[
NumberRange(min=0, max=150, message='年龄必须在0-150之间')
])
website = StringField('网站', validators=[
Optional(),
URL(message='URL格式不正确')
])
phone = StringField('手机号', validators=[
Regexp(r'^1[3-9]\d{9}$', message='手机号格式不正确')
])
submit = SubmitField('注册')
4.3 自定义验证器 #
python
from wtforms.validators import ValidationError
def validate_username(form, field):
"""验证用户名是否已存在"""
if User.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在')
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
validate_username # 自定义验证器
])
4.4 类内验证器 #
python
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
email = StringField('邮箱', validators=[DataRequired(), Email()])
def validate_username(self, field):
"""验证用户名"""
if User.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在')
def validate_email(self, field):
"""验证邮箱"""
if User.query.filter_by(email=field.data).first():
raise ValidationError('邮箱已注册')
五、表单渲染 #
5.1 基本渲染 #
html
<form method="POST">
{{ form.hidden_tag() }}
<div>
{{ form.username.label }}
{{ form.username() }}
</div>
<div>
{{ form.password.label }}
{{ form.password() }}
</div>
{{ form.submit() }}
</form>
5.2 添加CSS类 #
html
<!-- 方式1:在字段定义时 -->
username = StringField('用户名', render_kw={'class': 'form-control'})
<!-- 方式2:在模板中 -->
{{ form.username(class='form-control') }}
<!-- 方式3:使用class_参数 -->
{{ form.username(class_='form-control') }}
5.3 显示错误信息 #
html
<form method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.username.label(class='form-label') }}
{{ form.username(class='form-control') }}
{% if form.username.errors %}
{% for error in form.username.errors %}
<span class="text-danger">{{ error }}</span>
{% endfor %}
{% endif %}
</div>
{{ form.submit(class='btn btn-primary') }}
</form>
5.4 Bootstrap样式表单 #
html
<form method="POST">
{{ form.hidden_tag() }}
{% for field in form %}
{% if field.type == 'SubmitField' %}
{{ field(class='btn btn-primary') }}
{% elif field.type == 'BooleanField' %}
<div class="form-check">
{{ field(class='form-check-input') }}
{{ field.label(class='form-check-label') }}
</div>
{% else %}
<div class="form-group">
{{ field.label(class='form-label') }}
{{ field(class='form-control' + (' is-invalid' if field.errors else '')) }}
{% if field.errors %}
<div class="invalid-feedback">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</form>
六、文件上传 #
6.1 文件上传表单 #
python
from flask_wtf.file import FileField, FileAllowed, FileRequired
class UploadForm(FlaskForm):
avatar = FileField('头像', validators=[
FileRequired(message='请选择文件'),
FileAllowed(['jpg', 'png', 'gif'], message='只允许jpg、png、gif格式')
])
submit = SubmitField('上传')
6.2 处理文件上传 #
python
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
file = form.avatar.data
filename = secure_filename(file.filename)
file.save(os.path.join('uploads', filename))
return '上传成功'
return render_template('upload.html', form=form)
6.3 多文件上传 #
python
from flask_wtf.file import MultipleFileField
class MultiUploadForm(FlaskForm):
photos = MultipleFileField('照片', validators=[
FileAllowed(['jpg', 'png'], '只允许jpg、png格式')
])
submit = SubmitField('上传')
@app.route('/multi-upload', methods=['GET', 'POST'])
def multi_upload():
form = MultiUploadForm()
if form.validate_on_submit():
for file in form.photos.data:
filename = secure_filename(file.filename)
file.save(os.path.join('uploads', filename))
return '上传成功'
return render_template('multi_upload.html', form=form)
七、表单宏 #
7.1 创建表单宏 #
templates/macros/forms.html:
html
{% macro render_field(field) %}
<div class="form-group">
{{ field.label(class='form-label') }}
{{ field(class='form-control' + (' is-invalid' if field.errors else '')) }}
{% if field.errors %}
<div class="invalid-feedback">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endmacro %}
{% macro render_form(form, action='', method='POST') %}
<form action="{{ action }}" method="{{ method }}" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{% for field in form %}
{% if field.type == 'SubmitField' %}
{{ field(class='btn btn-primary') }}
{% elif field.type == 'BooleanField' %}
<div class="form-check">
{{ field(class='form-check-input') }}
{{ field.label(class='form-check-label') }}
</div>
{% elif field.type == 'CSRFTokenField' %}
{% else %}
{{ render_field(field) }}
{% endif %}
{% endfor %}
</form>
{% endmacro %}
7.2 使用表单宏 #
html
{% from 'macros/forms.html' import render_form %}
{{ render_form(form, action=url_for('register')) }}
八、表单继承 #
8.1 继承表单 #
python
class BaseForm(FlaskForm):
name = StringField('名称', validators=[DataRequired()])
description = TextAreaField('描述')
class ProductForm(BaseForm):
price = FloatField('价格', validators=[DataRequired()])
stock = IntegerField('库存', validators=[NumberRange(min=0)])
submit = SubmitField('保存')
class CategoryForm(BaseForm):
parent_id = SelectField('父分类', coerce=int)
submit = SubmitField('保存')
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 表单类 | 继承FlaskForm |
| 字段类型 | StringField、PasswordField等 |
| 验证器 | DataRequired、Email、Length等 |
| 表单渲染 | 使用模板渲染 |
| 文件上传 | FileField、FileAllowed |
| CSRF保护 | 自动启用 |
9.2 下一步 #
现在你已经掌握了Flask-WTF,接下来让我们学习 表单验证,深入了解验证机制!
最后更新:2026-03-28