WTForms是一个支持多个web框架的form组件,主要用于生成HTML标签,对用户请求数据进行验证。
安装
pip install wtforms
使用
用户注册示例
from flask import Flask
from flask import request
from flask import render_template
from wtforms import Form
from wtforms.fields import simple
from wtforms.fields import core
from wtforms import validators
app = Flask(__name__)
class RegisterForm(Form):
user = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空'),
validators.Length(min=6, max=16, message='用户名的长度为6-16'),
],
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空'),
validators.Regexp(
regex=r'(?=.*d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,16}',
message='密码中必须包含字母、数字、特殊字符,至少8个字符,最多16个字符',
)
],
)
pwd_confirm = simple.PasswordField(
label='确认密码',
validators=[
validators.DataRequired(message='密码不能为空'),
validators.EqualTo('pwd', message='两次密码输入不一致')
],
)
email = simple.StringField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空'),
validators.Email('邮箱格式错误'),
]
)
gender = core.RadioField(
label='性别',
choices=[(1,'男'),(2,'女')],
validators = [validators.DataRequired(message='性别不能为空')],
coerce=int # 将提交的字符串类型的数据,转换成int类型
)
city = core.SelectField(
label='城市',
choices=[
('bj','北京'),
('wh','武汉'),
('sh','上海'),
],
validators=[validators.DataRequired(message='城市不能为空')],
)
hobby = core.SelectMultipleField(
label='爱好',
choices=[
(1, '写代码'),
(2, '睡觉'),
(3, '吃饭'),
],
validators=[validators.DataRequired(message='爱好不能为空')],
coerce=int,
default=[1, 2]
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self._fields.items():
if name == 'gender':
continue
field.render_kw = {"class":"form-control"}
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == "GET":
form = RegisterForm(data={'city': 'bj'})
return render_template('register.html', form=form)
if request.method == 'POST':
form = RegisterForm(formdata=request.form)
if form.validate():
print(form.data)
return '验证成功'
else:
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
ul{
margin: 0;
padding: 0;
list-style-type: none;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<form class="form-horizontal col-md-8 col-md-offset-2" action="" method="post" novalidate>
{% for item in form %}
<div class="form-group">
<label for="{{ item.id }}" class="col-sm-2 control-label">{{item.label}}:</label>
<div class="col-sm-10">
{{ item }}
<span style="color: red">{{item.errors.0}}</span>
</div>
</div>
{% endfor%}
<div class="text-center">
<input type="submit" value="提交" class="btn btn-success">
</div>
</form>
</div>
</div>
</body>
</html>
CSRF_token
from wtforms.csrf.core import CSRF
from hashlib import md5
class MyCSRF(CSRF):
def setup_form(self, form):
self.csrf_context = form.meta.csrf_context()
self.csrf_secret = form.meta.csrf_secret
return super().setup_form(form)
def generate_csrf_token(self, csrf_token):
gid = self.csrf_secret + self.csrf_context
token = md5(gid.encode('utf-8')).hexdigest()
return token
def validate_csrf_token(self, form, field):
print(field.data, field.current_token)
if field.data != field.current_token:
raise ValueError('Invalid CSRF')
class LoginForm(Form):
user = simple.StringField(
render_kw={
'class': 'xxx',
'placeholder': '请输入用户名'
},
validators=[
validators.DataRequired(message='用户名不能为空'),
validators.Length(min=6, max=16, message='用户名长度在5-16位')
]
)
pwd = simple.PasswordField(
render_kw={
'class': 'xxx',
'placeholder': '请输入密码'
},
validators=[
validators.DataRequired(message='密码不能为空'),
validators.Regexp(
regex=r'(?=.*d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,16}',
message='密码中必须包含字母、数字、特殊字符,至少8个字符,最多16个字符',
)
]
)
class Meta:
csrf = True
csrf_field_name = 'csrf_token'
csrf_secret = 'xxxxxx'
csrf_context = lambda x: request.url
csrf_class = MyCSRF
模板文件中,在form
标签内添加一行
{{ form.csrf_token }}
钩子函数
validate_字段名
def validate_user(self, field):
print(field.data)
if field.data != 'xxxxxx':
# raise validators.ValidationError('用户名不是xxxxxx')
# 当前字段不再进行后续规则验证
raise validators.StopValidation('用户名不是xxxxxx')