表单处理
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>
下一步
学习数据库集成,将表单数据持久化存储。