Flask—wtform

越运锋
2023-12-01

一、wtform介绍

1. 定义

        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已经配置完成。

二、wtform模块介绍

1. 基本模块

 # 要继承的类
from wtforms import Form 
# 这里面包含了生成的DOM, 比如input, redio, select等
from wtforms.fields import simple, core  
# 校验器, 插件
from wtforms import validators, widgets

 

2. simple中的字段参数

'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  可选多个值的下拉列表

 3. validator各种校验器

        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 - 出现验证错误时引发的错误消息。

4. 自定义校验器

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])

三、实例

1. 实例一

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)

 2. 实例二

(1)form_base

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)
    )

(2)CBV

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)

(3)前端

<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>

 

 类似资料: