5

WTForms自定义验证方法(行内验证器)是如何被调用的?

 3 years ago
source link: https://greyli.com/how-custom-validator-work-in-wtforms/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

WTForms自定义验证方法(行内验证器)是如何被调用的?

这篇文章基于我在知乎上的这个回答,进行了相应的简化处理,放到这里做个备份。

万能的回答

答案在源码里。

简单的回答

WTForms会在你对表单实例调用Form.validate()方法时收集所有的行内验证方法,然后在对每个字段调用Field.validate()方法验证时将这些自定义行内验证方法一并和通过validators参数传入的验证器列表一起调用,进行验证。因为WTForms在调用时把对应的field作为参数传入了行内验证方法,所以你可以在自定义验证方法中通过field.data获取对应字段的数据。

深入的回答

WTForms会在你对表单实例调用Form.validate()方法时收集所有的行内验证方法。在Form类中的validate()方法定义中,你可以看到WTForms是如何收集这些行内验证方法的:

class Form(with_metaclass(FormMeta, BaseForm)):
   def validate(self):
        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)

源码位置:https://github.com/wtforms/wtforms/blob/2.2.1/wtforms/form.py#L305-L308

这里迭代所有的字段属性,然后表单类中是否包含validate_字段名形式的方法。如果有,那么就添加到extra字段里,这个字段被传递到BaseForm类的validate()方法中。在BaseForm类的validate()方法中,WTForms迭代所有字段,并对每个字段调用Field.validate()方法验证字段,继续传入自定义的行内验证方法:

class BaseForm(object):
    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):  # 调用字段类的验证方法进行验证,传入自定义验证器
                success = False
        return success

源码位置:https://github.com/wtforms/wtforms/blob/2.2.1/wtforms/form.py#L134-L154

而在字段基类Field的validate()方法中,WTForms使用itertool模块提供的chain()函数把你实例化字段类时传入的验证器(self.validators)和自定义行内验证器(extra_validators)连接到一起:

class Field(object):    
    def validate(self, form, extra_validators=tuple()):
        # Run validators
        if not stop_validation:
            chain = itertools.chain(self.validators, extra_validators)  # 合并两类验证器
            stop_validation = self._run_validation_chain(form, chain)  # 运行验证器

源码位置:https://github.com/wtforms/wtforms/blob/2.2.1/wtforms/fields/core.py#L204-L206

连接起来的所有验证器赋值为chain变量,并传入到self._run_validation_chain(form, chain)进行进一步调用:

class Field(object):    
    def _run_validation_chain(self, form, validators):
        for validator in validators:
                validator(form, self)  # 传入字段类本身作为第二个参数

源码位置:https://github.com/wtforms/wtforms/blob/2.2.1/wtforms/fields/core.py#L226

这个方法迭代所有的验证器对字段数据进行验证。关键在于validator(form, self),可以看到这里传入了第二个参数self,即Field类本身,这也是为什么你可以在自定义验证方法中通过field.data获取当前字段数据。

本条目发布于2018年7月12日。属于计算机与编程分类,被贴了 FlaskFlask-WTFPythonWTForms编程 标签。 ← Python中的下划线有多少种用法和含义? 使用Bootstrap-Flask在Flask项目中集成Bootstrap →

撰写评论 取消回复

电子邮件地址不会被公开,必填项已用*标出。

评论

姓名 *

电子邮件 *

站点

在此浏览器中保存我的名字、电邮和网站。

当有人回复我时,发送电子邮件提醒我。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK