flask wtforms组件详解

1、简介

  在flask内部并无提供全面的表单验证,因此当咱们不借助第三方插件来处理时候代码会显得混乱,而官方推荐的一个表单验证插件就是wtforms。wtfroms是一个支持多种web框架的form组件,主要用于对用户请求数据的进行验证,其的验证流程与django中的form表单验证由些许相似,本文将介绍wtforms组件使用方法以及验证流程。  html

wtforms依照功能类别来讲wtforms分别由如下几个类别:
  • Forms: 主要用于表单验证、字段定义、HTML生成,并把各类验证流程汇集在一块儿进行验证。
  • Fields: 主要负责渲染(生成HTML)和数据转换。
  • Validator:主要用于验证用户输入的数据的合法性。好比Length验证器能够用于验证输入数据的长度。
  • Widgets:html插件,容许使用者在字段中经过该字典自定义html小部件。
  • Meta:用于使用者自定义wtforms功能,例如csrf功能开启。
  • Extensions:丰富的扩展库,能够与其余框架结合使用,例如django。

2、安装使用

安装:
pip3 install wtforms

定义Forms

简单登录验证html5

app:python

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from  flask import Flask,render_template,request
from wtforms.fields import simple
from wtforms import Form
from wtforms import validators
from wtforms import widgets
app = Flask(__name__,template_folder="templates")

class LoginForm(Form):
    '''Form'''
    name = simple.StringField(
        label="用户名",
        widget=widgets.TextInput(),
        validators=[
            validators.DataRequired(message="用户名不能为空"),
            validators.Length(max=8,min=3,message="用户名长度必须大于%(max)d且小于%(min)d")
        ],
        render_kw={"class":"form-control"}  #设置属性生成的html属性
    )

    pwd = simple.PasswordField(
        label="密码",
        validators=[
            validators.DataRequired(message="密码不能为空"),
            validators.Length(max=18,min=4,message="密码长度必须大于%(max)d且小于%(min)d"),
            validators.Regexp(regex="\d+",message="密码必须是数字"),
        ],
        widget=widgets.PasswordInput(),
        render_kw={"class":"form-control"}
    )



@app.route('/login',methods=["GET","POST"])
def login():
    if request.method =="GET":
        form = LoginForm()
        return render_template("login.html",form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():  # 对用户提交数据进行校验,form.data是校验完成后的数据字典
            print("用户提交的数据用过格式验证,值为:%s"%form.data)
            return "登陆成功"
        else:
            print(form.errors,"错误信息")
        return render_template("login.html",form=form)


if __name__ == '__main__':
    app.run(debug=True)

login.htmlweb

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登陆</h1>
<form method="post">
    <!--<input type="text" name="name">-->
    <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>

    <!--<input type="password" name="pwd">-->
    <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
    <input type="submit" value="提交">
</form>
</body>
</html>

Form类实例化参数:django

  • formdata:须要被验证的form表单数据。
  • obj:当formdata参数为提供时候,可使用对象,也就是会是有obj.字段的值进行验证或设置默认值。
  • prefix: 字段前缀匹配,当传入该参数时,全部验证字段必须以这个开头(无太大意义)。
  • data: 当formdata参数和obj参数都有时候,可使用该参数传入字典格式的待验证数据或者生成html的默认值,列如:{'usernam':'admin’}。
  • meta:用于覆盖当前已经定义的form类的meta配置,参数格式为字典。 

自定义验证规则

#定义
class Myvalidators(object):
    '''自定义验证规则'''
    def __init__(self,message):
        self.message = message
    def __call__(self, form, field):
        print(field.data,"用户输入的信息")
        if field.data == "admin":
            raise validators.ValidationError(self.message)


#使用
class LoginForm(Form):
    '''Form'''
    name = simple.StringField(
        label="用户名",
        widget=widgets.TextInput(),
        validators=[ Myvalidators(message='用户名不能是admin'),]#自定义验证类
        render_kw={"class":"form-control"}  #设置属性
    )

字段介绍

wtforms中的Field类主要用于数据验证和字段渲染(生成html),如下是比较常见的字段:flask

  •  StringField    字符串字段,生成input要求字符串
  • PasswordField  密码字段,自动将输入转化为小黑点
  • DateField  日期字段,格式要求为datetime.date同样
  • IntergerField  整型字段,格式要求是整数
  • FloatField  文本字段,值是浮点数
  • BooleanField  复选框,值为True或者False
  • RadioField  一组单选框
  • SelectField  下拉列表,须要注意一下的是choices参数肯定了下拉选项,可是和HTML中的<select> 标签同样。
  • MultipleSelectField  多选字段,可选多个值的下拉列表
  • ...

字段参数:缓存

  • label:字段别名,在页面中能够经过字段.label展现;
  • validators:验证规则列表;
  • filters:过氯器列表,用于对提交数据进行过滤;
  • description:描述信息,一般用于生成帮助信息;
  • id:表示在form类定义时候字段的位置,一般你不须要定义它,默认会按照定义的前后顺序排序。
  • default:默认值
  • widget:html插件,经过该插件能够覆盖默认的插件,更多经过用户自定义;
  • render_kw:自定义html属性;
  • choices:复选类型的选项 ;

示例:session

from flask import Flask,render_template,redirect,request
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__,template_folder="templates")
app.debug = True

=======================simple===========================
class RegisterForm(Form):
    name = simple.StringField(
        label="用户名",
        validators=[
            validators.DataRequired()
        ],
        widget=widgets.TextInput(),
        render_kw={"class":"form-control"},
        default="wd"
    )
    pwd = simple.PasswordField(
        label="密码",
        validators=[
            validators.DataRequired(message="密码不能为空")
        ]
    )
    pwd_confim = simple.PasswordField(
        label="重复密码",
        validators=[
            validators.DataRequired(message='重复密码不能为空.'),
            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(RegisterForm,self).__init__(*args, **kwargs)  #继承父类的init方法
        self.favor.choices =((1, '篮球'), (2, '足球'), (3, '羽毛球'))  #把RegisterForm这个类里面的favor从新赋值,实现动态改变复选框中的选项

    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("密码不一致")  # 再也不继续后续验证

@app.route('/register',methods=["GET","POST"])
def register():
    if request.method=="GET":
        form = RegisterForm(data={'gender': 1})  #默认是1,
        return render_template("register.html",form=form)
    else:
        form = RegisterForm(formdata=request.form)
        if form.validate():  #判断是否验证成功
            print('用户提交数据经过格式验证,提交的值为:', form.data)  #全部的正确信息
        else:
            print(form.errors)  #全部的错误信息
        return render_template('register.html', form=form)

if __name__ == '__main__':
    app.run()

register.htmlapp

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0  50px">
    {% for item in form %}
    <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>
</html>

Meta

  Meta主要用于自定义wtforms的功能,大多都是配置选项,如下是配置参数:框架

csrf = True   # 是否自动生成CSRF标签
csrf_field_name = 'csrf_token'   # 生成CSRF标签name
csrf_secret = 'adwadada'     # 自动生成标签的值,加密用的csrf_secret
csrf_context = lambda x: request.url  # 自动生成标签的值,加密用的csrf_context
csrf_class = MyCSRF         # 生成和比较csrf标签     
locales = False      # 是否支持翻译
locales = ('zh', 'en')  # 设置默认语言环境
cache_translations = True  # 是否对本地化进行缓存
translations_cache = {}       # 保存本地化缓存信息的字段

示例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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):
    """
    Generate a CSRF token based on the user's IP. I am probably not very
    secure, so don't use me.
    """

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

3、实现原理

   wtforms实现原理这里主要从三个方面进行说明:form类建立过程、实例化过程、验证过程。从总体看其实现原理实则就是将每一个类别的功能(如Filed、validate、meta等)经过form进行组织、封装,在form类中调用每一个类别对象的方法实现数据的验证和html的渲染。这里先总结下验证流程:

  1. for循环每一个字段;
  2. 执行该字段的pre_validate钩子函数;
  3. 执行该字段参数的validators中的验证方法和validate_字段名钩子函数(若是有);
  4. 执行该字段的post_validate钩子函数;
  5. 完成当前字段的验证,循环下一个字段,接着走该字段的二、三、4流程,直到全部字段验证完成;

Form类建立过程

以示例中的RegisterForm为例子,它继承了Form:

class Form(with_metaclass(FormMeta, BaseForm)):
    Meta = DefaultMeta

    def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
        meta_obj = self._wtforms_meta()
        if meta is not None and isinstance(meta, dict):
            meta_obj.update_values(meta)
        super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)

        for name, field in iteritems(self._fields):
            # Set all the fields to attributes so that they obscure the class
            # attributes with the same names.
            setattr(self, name, field)
        self.process(formdata, obj, data=data, **kwargs)

    def __setitem__(self, name, value):
        raise TypeError('Fields may not be added to Form instances, only classes.')

    def __delitem__(self, name):
        del self._fields[name]
        setattr(self, name, None)

    def __delattr__(self, name):
        if name in self._fields:
            self.__delitem__(name)
        else:
            # This is done for idempotency, if we have a name which is a field,
            # we want to mask it by setting the value to None.
            unbound_field = getattr(self.__class__, name, None)
            if unbound_field is not None and hasattr(unbound_field, '_formfield'):
                setattr(self, name, None)
            else:
                super(Form, self).__delattr__(name)

    def validate(self):
        """
        Validates the form by calling `validate` on each field, passing any
        extra `Form.validate_<fieldname>` validators to the field validator.
        """
        extra = {}
        for name in self._fields:
            inline = getattr(self.__class__, 'validate_%s' % name, None)
            if inline is not None:
                extra[name] = [inline]

        return super(Form, self).validate(extra)

其中with_metaclass(FormMeta, BaseForm):

def with_metaclass(meta, base=object):
    return meta("NewBase", (base,), {})

这几段代码就等价于:

class Newbase(BaseForm,metaclass=FormMeta):
    pass

class Form(Newbase):
    pass

也就是说RegisterForm继承Form—》Form继承Newbase—》Newbase继承BaseForm,所以当解释器解释道class RegisterForm会执行FormMeta的__init__方法用于生成RegisterForm类:

class FormMeta(type):
    def __init__(cls, name, bases, attrs):
        type.__init__(cls, name, bases, attrs)
        cls._unbound_fields = None
        cls._wtforms_meta = None

由其__init__方法能够知道生成的RegisterForm中含有字段_unbound_fields和_wtforms_meta而且也包含了咱们本身定义的验证字段(name、pwd...),而且这些字段保存了每一个Field实例化的对象,如下拿name说明:

name = simple.StringField(
        label="用户名",
        validators=[
            validators.DataRequired()
        ],
        widget=widgets.TextInput(),
        render_kw={"class":"form-control"},
        default="wd"
    )

实例化StringField会先执行其__new__方法在执行__init__方法,而StringField继承了Field:

class Field(object):
    """
    Field base class
    """
    errors = tuple()
    process_errors = tuple()
    raw_data = None
    validators = tuple()
    widget = None
    _formfield = True
    _translations = DummyTranslations()
    do_not_call_in_templates = True  # Allow Django 1.4 traversal

    def __new__(cls, *args, **kwargs):
        if '_form' in kwargs and '_name' in kwargs:
            return super(Field, cls).__new__(cls)
        else:
            return UnboundField(cls, *args, **kwargs)

    def __init__(self, label=None, validators=None, filters=tuple(),
                 description='', id=None, default=None, widget=None,
                 render_kw=None, _form=None, _name=None, _prefix='',
                 _translations=None, _meta=None):

也就是这里会执行Field的__new__方法,在这里的__new__方法中,判断_form和_name是否在参数中,刚开始kwargs里面是label、validators这些参数,因此这里返回UnboundField(cls, *args, **kwargs),也就是这里的RegisterForm.name=UnboundField(),其余的字段也是相似,实际上这个对象是为了让咱们定义的字段由顺序而存在的,以下:

class UnboundField(object):
    _formfield = True
    creation_counter = 0

    def __init__(self, field_class, *args, **kwargs):
        UnboundField.creation_counter + 1
        self.field_class = field_class
        self.args = args
        self.kwargs = kwargs
        self.creation_counter = UnboundField.creation_counter

实例化该对象时候,会对每一个对象实例化的时候计数,第一个对象是1,下一个+1,并保存在每一个对象的creation_counter中。最后的RegisterForm中就保存了{’name’:UnboundField(1,simple.StringField,参数),’pwd’:UnboundField(2,simple.StringField,参数)…}。

Form类实例化过程

一样在RegisterForm实例化时候先执行__new__方法在执行__init__方法,这里父类中没也重写__new__也就是看__init__方法:

class Form(with_metaclass(FormMeta, BaseForm)):
    Meta = DefaultMeta

    def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
        meta_obj = self._wtforms_meta()  # 实例化meta
        if meta is not None and isinstance(meta, dict): # 判断meta是否存在且为字典
            meta_obj.update_values(meta) # 覆盖原meta的配置
            # 执行父类的构造方法
        super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)

        for name, field in iteritems(self._fields):
            # Set all the fields to attributes so that they obscure the class
            # attributes with the same names.
            setattr(self, name, field)
        self.process(formdata, obj, data=data, **kwargs)

构造方法中先实例化默认的meta,在判断是否传递类meta参数,传递则更新原meta的配置,接着执行父类的构造方法,父类是BaseForm:

class BaseForm(object):
    """
    Base Form Class.  Provides core behaviour like field construction,
    validation, and data and error proxying.
    """

    def __init__(self, fields, prefix='', meta=DefaultMeta()):
        if prefix and prefix[-1] not in '-_;:/.':
            prefix += '-'

        self.meta = meta  
        self._prefix = prefix
        self._errors = None
        self._fields = OrderedDict()

        if hasattr(fields, 'items'):
            fields = fields.items()

        translations = self._get_translations()
        extra_fields = []
        if meta.csrf:  #判断csrf配置是否为true,用于生成csrf的input框
            self._csrf = meta.build_csrf(self)
            extra_fields.extend(self._csrf.setup_form(self))
        #循环RegisterForm中的字段,并对每一个字段进行实例化
        for name, unbound_field in itertools.chain(fields, extra_fields):
            options = dict(name=name, prefix=prefix, translations=translations)
            field = meta.bind_field(self, unbound_field, options)
            self._fields[name] = field

在这里的for循环中执行meta.bind_field方法对每一个字段进行实例化,并以k,v的形式放入了self._fields属性中。而且实例化传递来参数_form和_name,也就是在执行BaseForm时候判断的两个属性,这里传递了就走正常的实例化过程。

def bind_field(self, form, unbound_field, options):
    """
    bind_field allows potential customization of how fields are bound.

    The default implementation simply passes the options to
    :meth:`UnboundField.bind`.

    :param form: The form.
    :param unbound_field: The unbound field.
    :param options:
        A dictionary of options which are typically passed to the field.

    :return: A bound field
    """
    return unbound_field.bind(form=form, **options)



def bind(self, form, name, prefix='', translations=None, **kwargs):
    kw = dict(
        self.kwargs,
        _form=form,  #传递_form 
        _prefix=prefix,
        _name=name, # 传递_name
        _translations=translations,
        **kwargs
    )
    return self.field_class(*self.args, **kw)

继续看Form类中的__init__方法,接着循环:

for name, field in iteritems(self._fields):
            # Set all the fields to attributes so that they obscure the class
            # attributes with the same names.
            setattr(self, name, field)
self.process(formdata, obj, data=data, **kwargs)

此时的self._fields已经包含了每一个实例化字段的对象,调用setattr为对象设置属性,为了方便获取字段,例如没有该语句获取字段时候经过RegisterForm()._fields[’name’],有了它直接经过RegisterForm().name获取,继续执行self.process(formdata, obj, data=data, **kwargs)方法,改方法用于验证的过程,由于此时的formdata、obj都是None,因此执行了该方法无影响。

 

验证流程

  当form对用户提交的数据验证时候,一样以上述注册为例子,此次请求是post,一样会走form = RegisterForm(formdata=request.form),可是此次不一样的是formdata已经有值,让咱们来看看process方法:

def process(self, formdata=None, obj=None, data=None, **kwargs):
    formdata = self.meta.wrap_formdata(self, formdata) 

    if data is not None: #判断data参数
        # XXX we want to eventually process 'data' as a new entity.
        #     Temporarily, this can simply be merged with kwargs.
        kwargs = dict(data, **kwargs),更新kwargs参数

    for name, field, in iteritems(self._fields):#循环每一个字段
        if obj is not None and hasattr(obj, name):# 判断是否有obj参数
            field.process(formdata, getattr(obj, name)) 
        elif name in kwargs:
            field.process(formdata, kwargs[name])
        else:
            field.process(formdata)

首先对用户提交的数据进行清洗变成k,v格式,接着判断data参数,若是不为空则将其值更新到kwargs中,而后循环self._fields(也就是咱们定义的字段),并执行字段的process方法:

def process(self, formdata, data=unset_value):
    self.process_errors = []
    if data is unset_value:
        try:
            data = self.default()
        except TypeError:
            data = self.default

    self.object_data = data

    try:
        self.process_data(data)
    except ValueError as e:
        self.process_errors.append(e.args[0])

    if formdata is not None:
        if self.name in formdata:
            self.raw_data = formdata.getlist(self.name)
        else:
            self.raw_data = []

        try:
            self.process_formdata(self.raw_data)
        except ValueError as e:
            self.process_errors.append(e.args[0])

    try:
        for filter in self.filters:
            self.data = filter(self.data)
    except ValueError as e:
        self.process_errors.append(e.args[0])

def process_data(self, value):
    self.data = value

该方法做用是将用户的提交的数据存放到data属性中,接下来就是使用validate()方法开始验证:

def validate(self):
    """
    Validates the form by calling `validate` on each field, passing any
    extra `Form.validate_<fieldname>` validators to the field validator.
    """
    extra = {}
    for name in self._fields: # 循环每一个field 
        #寻找当前类中以validate_’字段名匹配的方法’,例如pwd字段就寻找validate_pwd,也就是钩子函数
        inline = getattr(self.__class__, 'validate_%s' % name, None) 
        if inline is not None:
            extra[name] = [inline] #把钩子函数放到extra字典中
    return super(Form, self).validate(extra) #接着调用父类的validate方法

验证时候先获取全部每一个字段定义的validate_+'字段名'匹配的方法,并保存在extra字典中,在执行父类的validate方法:

 def validate(self, extra_validators=None):
        self._errors = None
        success = True
        for name, field in iteritems(self._fields):  # 循环字段的名称和对象
            if extra_validators is not None and name in extra_validators: # 判断该字段是否有钩子函数
                extra = extra_validators[name] # 获取到钩子函数
            else:
                extra = tuple()
            if not field.validate(self, extra): # 执行字段的validate方法
                success = False
        return success

该方法主要用于和须要验证的字段进行匹配,而后在执行每一个字段的validate方法:

 def validate(self, form, extra_validators=tuple()):
        self.errors = list(self.process_errors)
        stop_validation = False

        # Call pre_validate
        try:
            self.pre_validate(form)      # 先执行字段字段中的pre_validate方法,这是一个自定义钩子函数
        except StopValidation as e:
            if e.args and e.args[0]:
                self.errors.append(e.args[0])
            stop_validation = True
        except ValueError as e:
            self.errors.append(e.args[0])

        # Run validators
        if not stop_validation:     
            chain = itertools.chain(self.validators, extra_validators)     # 拼接字段中的validator和validate_+'字段名'验证
            stop_validation = self._run_validation_chain(form, chain)  # 执行每个验证规则,self.validators先执行

        # Call post_validate
        try:
            self.post_validate(form, stop_validation)
        except ValueError as e:
            self.errors.append(e.args[0])

        return len(self.errors) == 0

在该方法中,先会执行内部预留给用户自定义的字段的pre_validate方法,在将字段中的验证规则(validator也就是咱们定义的validators=[validators.DataRequired()],)和钩子函数(validate_+'字段名')拼接在一块儿执行,注意这里的validator先执行而字段的钩子函数后执行,咱们来看怎么执行的:

  def _run_validation_chain(self, form, validators):
       
        for validator in validators:  # 循环每一个验证规则
            try:
                validator(form, self)   # 传入提交数据并执行,若是是对象执行__call__,若是是函数直接调用
            except StopValidation as e:
                if e.args and e.args[0]:    
                    self.errors.append(e.args[0])   # 若是有错误,追加到总体错误中
                return True
            except ValueError as e:
                self.errors.append(e.args[0])

        return False            

很明显就是循环每个验证规则,并执行,有错误追加到总体错误中,接着咱们回到validate方法中,接着又会执行post_validate,这也是内置钩子函数,容许用户本身定义,最后这个字段的数据验证完成了,而在开始的for循环,循环结束意味着整个验证过程结束。
    def post_validate(self, form, validation_stopped):
        """
        Override if you need to run any field-level validation tasks after
        normal validation. This shouldn't be needed in most cases.

        :param form: The form the field belongs to.
        :param validation_stopped:
            `True` if any validator raised StopValidation.
        """
        pass
相关文章
相关标签/搜索