跳到主要内容

表单处理

Flask-WTF 提供了便捷的表单处理和 CSRF 保护功能。

安装

pip install flask-wtf

基本表单

定义表单类

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length

class LoginForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(message='用户名不能为空'),
Length(min=3, max=20, message='用户名长度3-20字符')
])
password = PasswordField('密码', validators=[
DataRequired(message='密码不能为空'),
Length(min=6, message='密码至少6位')
])
submit = SubmitField('登录')

视图函数中使用

from flask import render_template, redirect, url_for, flash

@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 验证成功
flash('登录成功!', 'success')
return redirect(url_for('index'))
return render_template('login.html', form=form)

模板渲染

<!-- templates/login.html -->
<form method="POST" action="">
{{ form.hidden_tag() }}

<div class="form-group">
{{ form.username.label }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
{% for error in form.username.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
{% endif %}
</div>

<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
{% for error in form.password.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
{% endif %}
</div>

{{ form.submit(class="btn btn-primary") }}
</form>

表单字段类型

from wtforms import (
StringField, # 文本输入
PasswordField, # 密码输入
TextAreaField, # 多行文本
IntegerField, # 整数
FloatField, # 浮点数
BooleanField, # 复选框
SelectField, # 下拉选择
SelectMultipleField, # 多选
RadioField, # 单选
DateField, # 日期
DateTimeField, # 日期时间
FileField, # 文件上传
SubmitField # 提交按钮
)

验证器

from wtforms.validators import (
DataRequired, # 必填
Email, # 邮箱格式
Length, # 长度限制
EqualTo, # 值相等(确认密码)
NumberRange, # 数值范围
URL, # URL格式
Regexp, # 正则表达式
Optional, # 可选
InputRequired # 输入必填(与DataRequired不同)
)

完整注册表单示例

class RegistrationForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
Length(min=3, max=20),
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
'用户名必须以字母开头,只能包含字母、数字、下划线和点')
])
email = StringField('邮箱', validators=[
DataRequired(),
Email()
])
password = PasswordField('密码', validators=[
DataRequired(),
Length(min=6),
])
password2 = PasswordField('确认密码', validators=[
DataRequired(),
EqualTo('password', message='两次输入的密码不一致')
])
submit = SubmitField('注册')

# 自定义验证方法
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('用户名已存在')

def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('邮箱已被注册')

文件上传

from flask_wtf.file import FileField, FileRequired, FileAllowed

class UploadForm(FlaskForm):
photo = FileField('上传头像', validators=[
FileRequired(),
FileAllowed(['jpg', 'png', 'gif'], '只允许图片文件')
])
submit = SubmitField('上传')

@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
filename = secure_filename(form.photo.data.filename)
form.photo.data.save('uploads/' + filename)
flash('上传成功!')
return redirect(url_for('index'))
return render_template('upload.html', form=form)

动态表单

class DynamicForm(FlaskForm):
name = StringField('名称')
submit = SubmitField('提交')

# 动态添加字段
@app.route('/dynamic', methods=['GET', 'POST'])
def dynamic():
form = DynamicForm()

# 动态添加字段
for i in range(3):
field_name = f'extra_{i}'
setattr(DynamicForm, field_name, StringField(f'额外字段 {i}'))

if form.validate_on_submit():
# 处理数据
pass

return render_template('dynamic.html', form=form)

CSRF 保护

配置密钥

app.config['SECRET_KEY'] = 'your-secret-key'

模板中添加 CSRF 令牌

<form method="POST">
{{ form.hidden_tag() }} <!-- 包含 CSRF 令牌 -->
<!-- 或手动添加 -->
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
</form>

AJAX 请求中的 CSRF

// 从 meta 标签获取 CSRF 令牌
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

fetch('/api/endpoint', {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken
},
body: JSON.stringify(data)
});

表单宏

<!-- templates/_formhelpers.html -->
{% macro render_field(field) %}
<div class="form-group">
{{ field.label(class="form-label") }}
{{ field(class="form-control" + (' is-invalid' if field.errors else ''), **kwargs) }}
{% if field.errors %}
<div class="invalid-feedback">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endmacro %}

使用:

{% from "_formhelpers.html" import render_field %}

<form method="POST">
{{ form.hidden_tag() }}
{{ render_field(form.username) }}
{{ render_field(form.email) }}
{{ render_field(form.password) }}
{{ form.submit(class="btn btn-primary") }}
</form>

下一步

学习数据库集成,将表单数据持久化存储。