如果 WTForms 被定义为验证字段,它如何知道使用 validate_<field name>?

How does WTForms know to use validate_<field name> if it is defined to validate a field?

当我使用 WTForms 定义表单时,我可以将 validate_<field name> 方法添加到 subclass,并且 WTForms 知道使用它来验证命名字段。我觉得这很有趣,因为方法的名称取决于字段 class 属性的名称。它是如何解决这个问题的?

class UploadForm(Form):
    image = FileField("image file")
    submit = SubmitField("Submit")

    def validate_image(self,field):
        if field.data.filename[-4:].lower() != ".jpg":
            raise ValidationError("nope not allowed")

所有 Python 类型的所有成员实际上都是哈希表 (dicts) 并且所有类型信息都在运行时具体化。因此,您可以从代码中检查任何 Python class。

作为一个快速交互示例:

>>> class Foo(object):
...   my_attribute = 'Something'
... 
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'my_attribute']
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', 'my_attribute': 'Something', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None})
>>> [x for x in Foo.__dict__ if not x.startswith('__')]
['my_attribute']
>>> 

WTForms 在调用时检查 class(调用 class 创建实例:form = Form())并记录字段及其名称。然后在验证期间,它会查看实例是否具有方法 validate_<field_name>.

FormMeta.__call__, it uses the dir 函数中列出在 class 对象上定义的名称并记录字段。

for name in dir(cls):  # look at the names on the class
    if not name.startswith('_'):  # ignore names with leading _
        unbound_field = getattr(cls, name)  # get the value
        if hasattr(unbound_field, '_formfield'):  # make sure it's a field
            fields.append((name, unbound_field))  # record it

Form.validate it uses the getattr 函数中尝试为它记录的每个字段获取名称 validate_<field name> 的值。

for name in self._fields:  # go through recorded field names
    # get method with name validate_<field name> or None
    inline = getattr(self.__class__, 'validate_%s' % name, None)
    if inline is not None:  # if there is such a method record it
        extra[name] = [inline]