Flask表单验证 #
一、验证概述 #
1.1 为什么需要验证 #
| 原因 | 说明 |
|---|---|
| 数据完整性 | 确保必填字段有值 |
| 数据格式 | 确保数据格式正确 |
| 安全性 | 防止恶意输入 |
| 用户体验 | 提供友好的错误提示 |
1.2 验证时机 #
python
@app.route('/form', methods=['GET', 'POST'])
def form_view():
form = MyForm()
# validate_on_submit() 会同时检查:
# 1. 是否是POST请求
# 2. 表单数据是否通过验证
if form.validate_on_submit():
# 验证通过,处理数据
pass
return render_template('form.html', form=form)
二、内置验证器 #
2.1 DataRequired #
python
from wtforms.validators import DataRequired
username = StringField('用户名', validators=[
DataRequired(message='用户名不能为空')
])
2.2 Length #
python
from wtforms.validators import Length
username = StringField('用户名', validators=[
Length(min=3, max=20, message='用户名长度必须在3-20个字符之间')
])
# 只限制最小长度
password = PasswordField('密码', validators=[
Length(min=6, message='密码至少6个字符')
])
2.3 Email #
python
from wtforms.validators import Email
email = StringField('邮箱', validators=[
Email(message='请输入有效的邮箱地址')
])
2.4 NumberRange #
python
from wtforms.validators import NumberRange
age = IntegerField('年龄', validators=[
NumberRange(min=0, max=150, message='年龄必须在0-150之间')
])
price = FloatField('价格', validators=[
NumberRange(min=0, message='价格不能为负数')
])
2.5 URL #
python
from wtforms.validators import URL
website = StringField('网站', validators=[
URL(message='请输入有效的URL')
])
# 要求特定协议
website = StringField('网站', validators=[
URL(require_tld=True, message='请输入有效的URL')
])
2.6 EqualTo #
python
from wtforms.validators import EqualTo
password = PasswordField('密码')
confirm = PasswordField('确认密码', validators=[
EqualTo('password', message='两次密码输入不一致')
])
2.7 Regexp #
python
from wtforms.validators import Regexp
phone = StringField('手机号', validators=[
Regexp(r'^1[3-9]\d{9}$', message='请输入有效的手机号')
])
username = StringField('用户名', validators=[
Regexp(r'^[a-zA-Z][a-zA-Z0-9_]*$',
message='用户名必须以字母开头,只能包含字母、数字和下划线')
])
2.8 Optional #
python
from wtforms.validators import Optional
nickname = StringField('昵称', validators=[
Optional()
])
2.9 AnyOf #
python
from wtforms.validators import AnyOf
status = SelectField('状态', validators=[
AnyOf(['active', 'inactive', 'pending'], message='无效的状态')
])
2.10 NoneOf #
python
from wtforms.validators import NoneOf
username = StringField('用户名', validators=[
NoneOf(['admin', 'root', 'system'], message='该用户名不可用')
])
三、组合验证器 #
3.1 多个验证器 #
python
username = StringField('用户名', validators=[
DataRequired(message='用户名不能为空'),
Length(min=3, max=20, message='用户名长度3-20个字符'),
Regexp(r'^[a-zA-Z][a-zA-Z0-9_]*$', message='用户名格式不正确')
])
3.2 条件验证 #
python
from wtforms.validators import Optional, DataRequired
class ProfileForm(FlaskForm):
nickname = StringField('昵称', validators=[Optional()])
# 如果填写了昵称,则验证长度
def validate_nickname(self, field):
if field.data and len(field.data) > 50:
raise ValidationError('昵称不能超过50个字符')
四、自定义验证器 #
4.1 函数验证器 #
python
from wtforms.validators import ValidationError
def validate_username_exists(form, field):
"""验证用户名是否已存在"""
user = User.query.filter_by(username=field.data).first()
if user:
raise ValidationError('用户名已存在')
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
validate_username_exists
])
4.2 类验证器 #
python
class UniqueValidator:
"""唯一性验证器"""
def __init__(self, model, field, message=None):
self.model = model
self.field = field
self.message = message or '该值已存在'
def __call__(self, form, field):
instance = self.model.query.filter(
getattr(self.model, self.field) == field.data
).first()
if instance:
raise ValidationError(self.message)
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
UniqueValidator(User, 'username', '用户名已存在')
])
email = StringField('邮箱', validators=[
DataRequired(),
Email(),
UniqueValidator(User, 'email', '邮箱已注册')
])
4.3 类内验证方法 #
python
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
email = StringField('邮箱', validators=[DataRequired(), Email()])
password = PasswordField('密码', validators=[DataRequired()])
confirm = PasswordField('确认密码', validators=[DataRequired()])
def validate_username(self, field):
"""验证用户名"""
if User.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在')
if field.data.lower() in ['admin', 'root', 'system']:
raise ValidationError('该用户名不可用')
def validate_email(self, field):
"""验证邮箱"""
if User.query.filter_by(email=field.data).first():
raise ValidationError('邮箱已注册')
def validate_confirm(self, field):
"""验证确认密码"""
if field.data != self.password.data:
raise ValidationError('两次密码输入不一致')
五、高级验证 #
5.1 密码强度验证 #
python
import re
class PasswordStrength:
"""密码强度验证器"""
def __init__(self, min_strength='medium', message=None):
self.min_strength = min_strength
self.message = message or '密码强度不足'
self.strengths = {
'weak': 1,
'medium': 2,
'strong': 3
}
def __call__(self, form, field):
password = field.data
strength = 0
# 检查长度
if len(password) >= 8:
strength += 1
# 检查是否包含数字
if re.search(r'\d', password):
strength += 1
# 检查是否包含小写字母
if re.search(r'[a-z]', password):
strength += 1
# 检查是否包含大写字母
if re.search(r'[A-Z]', password):
strength += 1
# 检查是否包含特殊字符
if re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
strength += 1
required = self.strengths.get(self.min_strength, 2)
if strength < required:
raise ValidationError(self.message)
class RegisterForm(FlaskForm):
password = PasswordField('密码', validators=[
DataRequired(),
PasswordStrength(min_strength='medium', message='密码必须包含字母和数字,至少8个字符')
])
5.2 日期验证 #
python
from datetime import datetime
class DateRange:
"""日期范围验证器"""
def __init__(self, min_date=None, max_date=None, message=None):
self.min_date = min_date
self.max_date = max_date
self.message = message
def __call__(self, form, field):
date = field.data
if self.min_date and date < self.min_date:
raise ValidationError(self.message or f'日期不能早于{self.min_date}')
if self.max_date and date > self.max_date:
raise ValidationError(self.message or f'日期不能晚于{self.max_date}')
class EventForm(FlaskForm):
start_date = DateField('开始日期', validators=[
DataRequired(),
DateRange(min_date=datetime.now().date(), message='开始日期不能早于今天')
])
end_date = DateField('结束日期', validators=[
DataRequired()
])
def validate_end_date(self, field):
if field.data < self.start_date.data:
raise ValidationError('结束日期不能早于开始日期')
5.3 文件验证 #
python
from flask_wtf.file import FileAllowed, FileRequired, FileSize
class AvatarForm(FlaskForm):
avatar = FileField('头像', validators=[
FileRequired(message='请选择文件'),
FileAllowed(['jpg', 'png', 'gif'], message='只允许jpg、png、gif格式'),
FileSize(max_size=2*1024*1024, message='文件大小不能超过2MB')
])
六、验证错误处理 #
6.1 获取错误信息 #
python
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
# 验证通过
pass
else:
# 获取所有错误
errors = form.errors
# {'username': ['用户名已存在'], 'email': ['邮箱格式不正确']}
# 获取特定字段错误
username_errors = form.username.errors
return render_template('register.html', form=form)
6.2 模板中显示错误 #
html
<form method="POST">
{{ form.hidden_tag() }}
{% for field in form %}
{% if field.errors %}
<div class="form-group has-error">
{{ field.label }}
{{ field(class='form-control is-invalid') }}
<div class="invalid-feedback">
{% for error in field.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
</div>
{% else %}
<div class="form-group">
{{ field.label }}
{{ field(class='form-control') }}
</div>
{% endif %}
{% endfor %}
</form>
6.3 Flash消息 #
python
from flask import flash
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
try:
user = create_user(form.data)
flash('注册成功!', 'success')
return redirect(url_for('login'))
except Exception as e:
flash(f'注册失败: {str(e)}', 'danger')
else:
for field, errors in form.errors.items():
for error in errors:
flash(f'{getattr(form, field).label.text}: {error}', 'danger')
return render_template('register.html', form=form)
七、最佳实践 #
7.1 验证器组织 #
python
# validators.py
from wtforms.validators import ValidationError
def username_exists(form, field):
"""验证用户名是否存在"""
if User.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在')
def email_exists(form, field):
"""验证邮箱是否存在"""
if User.query.filter_by(email=field.data).first():
raise ValidationError('邮箱已注册')
# forms.py
from validators import username_exists, email_exists
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
Length(min=3, max=20),
username_exists
])
email = StringField('邮箱', validators=[
DataRequired(),
Email(),
email_exists
])
7.2 错误消息国际化 #
python
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(message=gettext('用户名不能为空')),
Length(min=3, max=20, message=gettext('用户名长度必须在%(min)d-%(max)d之间'))
])
八、总结 #
8.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 内置验证器 | DataRequired、Email、Length等 |
| 自定义验证器 | 函数或类实现 |
| 类内验证 | validate_fieldname方法 |
| 错误处理 | form.errors获取错误 |
| 组合验证 | 多个验证器组合使用 |
8.2 下一步 #
现在你已经掌握了表单验证,接下来让我们学习 文件上传,了解如何处理文件上传!
最后更新:2026-03-28