首先说明wtf不同于flask-wtf,前者时后者的加强版
下面说明常用字段,钩子函数,重写__init__方法
from flask import Flask, render_template, request
from wtforms import Form
from wtforms import validators
from wtforms import widgets
from wtforms.fields import simple
from wtforms.fields import core
from wtforms.fields import html5
app = Flask(__name__)
class LoginForm(Form):
name = simple.StringField(
label='用户名:', # input标签,前端 .字段.label
validators=[
validators.DataRequired(message='用户名不能为空'), # 为空错误信心
validators.Length(min=2, max=6, message='用户名长度必须大于%(min)d且小于%(max)d'), # 长度错误信息
],
widget=widgets.TextInput(), # 插件
render_kw={'class': 'form-control'}, # 自定义属性
default='aki' # 默认值
)
pwd = simple.PasswordField(
label='密码:',
validators=[
validators.DataRequired(message='密码不为空'),
validators.Length(min=8, message='密码不少于八位'),
# validators.Regexp(regex="正则匹配")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
pwd_confirm = simple.PasswordField(
label='重复密码',
validators=[
validators.DataRequired(message='重复密码'),
validators.EqualTo('pwd', message='两次密码不一致'), # 这个函数自动校验和哪个字段是否相同
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
# 单选
gender = core.RadioField(
label='性别',
choices=(
(1, '男'),
(2, '女'),
),
coerce=int # 前端转来的数据是字符串,转成int
)
# 下拉框
city = core.SelectField(
label='城市',
choices=(
('bj', '北京'),
('sh', '上海'),
)
)
# 多选框
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
# 多选框
favor = core.SelectMultipleField(
label='喜好',
choices=(
(1, '篮球'),
(2, '足球'),
),
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput(),
coerce=int,
default=[1, 2]
)
# 重写__init__方法,解决更新数据库,前端拿到数据无法刷新的问题,静态字段问题,本身不会影响表单
# def __init__(self, *args, **kwargs):
# super(LoginForm, self).__init__(*args, **kwargs)
# self.city.choices = '数据库查询的方法'
# 自定义构造函数 必须按 validate_字段名 格式
def validate_name(self, field):
print("aa", field.data) # 字段内容
if not field.data.startswith('x'):
raise validators.ValidationError('必须以x开头')
# 对同一个字段继续后续验证
# raise validators.StopValidation('xx')
# 对同一字段不在继续后面的验证
@app.route('/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('index.html', form=form)
form = LoginForm(formdata=request.form)
if form.validate():
print("验证通过的数据", form.data)
return "ok"
else:
print(form.errors)
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run()
前端显示
<form method="POST">
{% for field in form %}
{{ field.label }} : {{ field }}
{{ field.error[0] }}
{% endfor %}
StringField | 文本字段 |
TextAreaField | 多行文本字 |
PasswordField | 密码文本字 |
HiddenField | 隐藏文本字段 |
的DateField | 文本字段,值为datetime.date格 |
DateTimeField字段 | 文本字段,值为datetime.datetime格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatFiel | 文本字段,值为浮点数 |
BooleanField | 复选框,值为真和假 |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表,可选择多个值 |
的FileField | 文件上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList中 | 一组指定类型的字段 |
电子邮件 | 验证电子邮件地址 |
等于 | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
IP地址 | 验证IPv4网络地址 |
长度 | 验证输入字符串的长度 |
NumberRange | 验证输入的值在数字范围内 |
可选的 | 无输入值时跳过其他验证函数 |
需要 | 确保字段中有数据 |
正则表达式 | 使用正则表达式验证输入值 |
网址 | 验证URL |
任何 | 确保输入值在可选值列表中 |
没有 | 确保输入值不在可选值列表中 |
DataRequired | 确保转换类型后字段中有数据 |
InputRequired | 确保转换类型前字段中有数据 |
MAC地址 | 验证MAC地址 |
UUID | 验证UUID |
下面是带CSRF的
from flask import Flask, render_template, request, redirect, session
from wtforms import Form
from wtforms.csrf.core import CSRF
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
from hashlib import md5
app = Flask(__name__, template_folder='templates')
app.debug = True
class MyCSRF(CSRF):
def setup_form(self, form):
self.csrf_context = form.meta.csrf_context()
self.csrf_secret = form.meta.csrf_secret
return super(MyCSRF, self).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 TestForm(Form):
name = html5.EmailField(label='用户名')
pwd = simple.StringField(label='密码')
class Meta:
# -- CSRF
# 是否自动生成CSRF标签
csrf = True
# 生成CSRF标签name
csrf_field_name = 'csrf_token'
# 自动生成标签的值,加密用的csrf_secret
csrf_secret = 'xxxxxx'
# 自动生成标签的值,加密用的csrf_context
csrf_context = lambda x: request.url
# 生成和比较csrf标签
csrf_class = MyCSRF
# -- i18n
# 是否支持本地化
# locales = False
locales = ('zh', 'en')
# 是否对本地化进行缓存
cache_translations = True
# 保存本地化缓存信息的字段
translations_cache = {}
@app.route('/index/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
form = TestForm()
else:
form = TestForm(formdata=request.form)
if form.validate():
print(form)
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run()