这篇文章基于我在知乎上的这个回答,进行了相应的简化处理,放到这里做个备份。
万能的回答
答案在源码里。
简单的回答
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:
try:
validator(form, self) # 传入字段类本身作为第二个参数
源码位置:https://github.com/wtforms/wtforms/blob/2.2.1/wtforms/fields/core.py#L226
这个方法迭代所有的验证器对字段数据进行验证。关键在于validator(form, self),可以看到这里传入了第二个参数self,即Field类本身,这也是为什么你可以在自定义验证方法中通过field.data获取当前字段数据。