flask设计了WTForm表单库来使flask可以更加简便地管理操作表单数据。WTForm中最重要的几个概念如下:
Form类,开发者自定义的表单必须继承自Form类或者其子类。Form类最主要的功能是通过其所包含的Field类提供对表单内数据的快捷访问方式。
各种Field类,即字段。一般而言每个Field类都对应一个input的HTML标签。比如WTForm自带的一些Field类比如BooleanField就对应<input type="checkbox">,SubmitField就对应<input type="submit">等等。
Validator类。这个类用于验证用户输入的数据的合法性。比如Length验证器可以用于验证输入数据的长度,FileAllowed验证上传文件的类型等等。
另外,flask为了防范csrf(cross-site request forgery)攻击,默认在使用flask-wtf之前要求app一定要设置过secret_key。最简单地可以通过app.config['SECRET_KEY'] = 'xxxx'来配置。app的配置涉及到如何架构整个项目目录,在以后再讲,这里默认这个SECRET_KEY已经配置完成。
# 要继承的类
from wtforms import Form
# 这里面包含了生成的DOM, 比如input, redio, select等
from wtforms.fields import simple, core
# 校验器, 插件
from wtforms import validators, widgets
'BooleanField', 'TextAreaField', 'PasswordField', 'FileField', 'MultipleFileField','HiddenField', 'SubmitField', 'TextField'
PasswordField 密码字段,自动将输入转化为小黑点
DateField 文本字段,格式要求为datetime.date一样
IntergerField 文本字段,格式要求是整数
DecimalField 文本字段,格式要求和decimal.Decimal一样
FloatField 文本字段,值是浮点数
BooleanField 复选框,值为True或者False
RadioField 一组单选框
SelectField 下拉列表,需要注意一下的是choices参数确定了下拉选项,但是和HTML中的<select> 标签一样,其是一个tuple组成的列表,可以认为每个tuple的第一项是选项的真正的值,而第二项是alias。
MultipleSelectField 可选多个值的下拉列表
Validator是验证函数,把一个字段绑定某个验证函数之后,flask会在接收表单中的数据之前对数据做一个验证,如果验证成功才会接收数据。验证函数Validator如下,具体的validator可能需要的参数不太一样,这里只给出一些常用的,更多详细的用法可以参见wtforms/validators.py文件的源码,参看每一个validator类需要哪些参数;*基本上每一个validator都有message参数,指出当输入数据不符合validator要求时显示什么信息。
1、Email
验证电子邮件地址的合法性,要求正则模式是^.+@([^.@][^@]+)$
2、EqualTo
比较两个字段的值,通常用于输入两次密码等场景,可写参数fieldname,不过注意其是一
个字符串变量,指向同表单中的另一个字段的字段名
3、IPAddress
验证IPv4地址,参数默认ipv4=True,ipv6=False.如果想要验证ipv6可以设置这两个参数
反过来
4、Length
验证输入的字符串的长度,可以有min,max两个参数指出要设置的长度下限和上限,注意参
数类型是字符串,不是INT!!
5、NumberRange
验证输入数字是否在范围内,可以有min和max两个参数指出数字上限下限,注意参数类型
是字符串,不是INT然后在这个validator的message参数里可以设置%(min)s和%(max)s
两个格式化部分,来告诉前端这个范围到底是多少。
6、DataRequired
必填字段
7、InputRequired
验证是否为此字段提供了输入
8、Regexp(regex,flags = 0,message = None )
根据用户提供的regexp验证字段。
regex - 要使用的正则表达式字符串。也可以是编译正则表达式模式。
flags - 要使用的regexp标志,例如re.IGNORECASE如果正则表达式不是字符串,则忽略
message - 出现验证错误时引发的错误消息。
class MyForm(Form):
name = StringField('Name', [InputRequired()])
def validate_name(form, field):
if len(field.data) > 50:
raise ValidationError('Name must be less than 50 characters')
def my_length_check(form, field):
if len(field.data) > 50:
raise ValidationError('Field must be less than 50 characters')
class MyForm(Form):
name = StringField('Name', [InputRequired(), my_length_check])
from flask import Blueprint,render_template,request
from wtforms.fields import simple
from wtforms.fields import html5
from flask_wtf import FlaskForm
from wtforms.fields import core
from wtforms import StringField
from wtforms import widgets,validators
class MyForm(FlaskForm):
# == == == == == == == == == == == =simple == == == == == == == == ==
name = StringField(
label='姓名:',
validators=[validators.DataRequired()],
widget=widgets.TextInput(),
render_kw={"class": "form-control"}, # 添加一些样式,用于Bootstrap
)
pwd = simple.PasswordField(
label="密码",
validators=[
validators.DataRequired(message="密码不能为空")
]
)
pwd_confim = simple.PasswordField(
label="重复密码",
validators=[
validators.DataRequired(message='重复密码不能为空.'),
# 注意:EqualTo('pwd')这需要时密码名称字符串格式
validators.EqualTo('pwd', message="两次密码不一致")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
#========================html5============================
email = html5.EmailField( # 注意这里用的是html5.EmailField
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
#===================以下是用core来调用的=======================
gender = core.RadioField(
label="性别",
choices=(
(1,"男"),
(1,"女"),
),
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]
)
def __init__(self, *args, **kwargs): # 这里的self是一个RegisterForm对象
'''重写__init__方法'''
super(MyForm, self).__init__(*args, **kwargs) # 继承父类的init方法
# 吧RegisterForm这个类里面的favor重新赋值
self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
# 方法验证
def validate_pwd_confim(self, field, ):
'''
自定义pwd_config字段规则,例:与pwd字段是否一致
:param field:
:return:
'''
# 最开始初始化时,self.data中已经有所有的值
if field.data != self.data['pwd']:
# 继续后续验证
# raise validators.ValidationError("密码不一致")
# 不再继续后续验证
raise validators.StopValidation("密码不一致")
register = Blueprint("register",__name__)
@register.route("/regist",methods=["GET","POST"])
def regist():
if request.method == "GET":
form = MyForm()
return render_template("regis.html",forms = form)
form = MyForm(formdata=request.form)
if form.validate(): #验证是否成功
print("数据校验通过")
else:
return render_template("regis.html",forms = form)
from wtforms import Form, validators, widgets
from wtforms.fields import simple, core
class Reg(Form):
username = simple.StringField(
label="用户名",
validators=[
validators.DataRequired(message="用户名不能为空")
],
render_kw={"class": "my_class"}
)
password = simple.PasswordField(
label="密码",
validators=[
validators.Length(min=6, max=16, message="最少6,最多16")
],
)
repassword = simple.PasswordField(
label="确认密码",
validators=[
validators.EqualTo("password", message="两次密码不一致")
],
)
gender = core.SelectField(
label="性别",
choices=(
(1, "女"),
(2, "男")
),
default=1,
render_kw={"class": "my_class"},
coerce=int
)
email = simple.StringField(
label="邮箱",
validators=[
validators.DataRequired(message="邮箱不能为空"),
validators.Email(message="邮箱格式错误")
],
)
hobby = core.SelectMultipleField(
label="爱好",
choices=(
(1, "足球"),
(2, "篮球"),
(3, "排球"),
),
coerce=int,
default=(1, 3)
)
from flask import Blueprint, views, render_template, request
from .Form_base import Reg
my_blueprint = Blueprint("admin", __name__)
@my_blueprint.route("/register")
class Register(views.MethodView):
def get(self):
# 将form类实例化,把表格数据传入前端
reg_obj = Reg()
return render_template("register.html", reg_obj=reg_obj)
def post(self):
reg_data = Reg(request.form)
if reg_data.validate():
print(reg_data.data.get("hobby"))
print(reg_data.data.get("gender"))
return "注册成功!"
else:
# 有校验不符则将错误数据传给前端
return render_template("register.html", reg_obj=reg_data)
<style>
.my_class{
width: 500px;
}
</style>
<form action="" method="post">
{% for filed in reg_obj %}
<p>{{ filed.label }}{{ filed }}{{ filed.errors.0 }}</p>
{% endfor %}
<input type="submit" name="提交">
</form>