<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Flask-WTF &#8211; 李辉 / Grey Li</title>
	<atom:link href="https://greyli.com/tag/flask-wtf/feed/" rel="self" type="application/rss+xml" />
	<link>https://greyli.com</link>
	<description>一个编程和写作爱好者的在线记事本</description>
	<lastBuildDate>Thu, 06 Nov 2025 11:36:11 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.26</generator>

<image>
	<url>https://greyli.com/wp-content/uploads/2025/03/avatar-500-compressed-144x144.jpg</url>
	<title>Flask-WTF &#8211; 李辉 / Grey Li</title>
	<link>https://greyli.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WTForms自定义验证方法（行内验证器）是如何被调用的？</title>
		<link>https://greyli.com/how-custom-validator-work-in-wtforms/</link>
		<comments>https://greyli.com/how-custom-validator-work-in-wtforms/#respond</comments>
		<pubDate>Thu, 12 Jul 2018 10:16:35 +0000</pubDate>
		<dc:creator><![CDATA[李辉]]></dc:creator>
				<category><![CDATA[计算机与编程]]></category>
		<category><![CDATA[Flask]]></category>
		<category><![CDATA[Flask-WTF]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[WTForms]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://greyli.com/?p=1754</guid>
		<description><![CDATA[这篇文章基于我在知乎上的这个回答，进行了相应的简化处理，放到这里做个备份。 万能的回答 答案在源码里。 简单的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div class="RichContent-inner">
<p>这篇文章基于我在知乎上的<a href="https://www.zhihu.com/question/280470162/answer/418393736">这个回答</a>，进行了相应的简化处理，放到这里做个备份。</p>
<h2>万能的回答</h2>
<p>答案在源码里。</p>
<h2>简单的回答</h2>
<p>WTForms会在你对表单实例调用Form.validate()方法时收集所有的行内验证方法，然后在对每个字段调用Field.validate()方法验证时将这些自定义行内验证方法一并和通过validators参数传入的验证器列表一起调用，进行验证。因为WTForms在调用时把对应的field作为参数传入了行内验证方法，所以你可以在自定义验证方法中通过field.data获取对应字段的数据。</p>
<h2>深入的回答</h2>
<p>WTForms会在你对表单实例调用Form.validate()方法时收集所有的行内验证方法。在Form类中的validate()方法定义中，你可以看到WTForms是如何收集这些行内验证方法的：</p>
<div>
<pre class="">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)
</pre>
</div>
<blockquote><p>源码位置：<a class=" external" href="https://link.zhihu.com/?target=https%3A//github.com/wtforms/wtforms/blob/2.2.1/wtforms/form.py%23L305-L308" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043"><span class="invisible">https://</span><span class="visible">github.com/wtforms/wtfo</span><span class="invisible">rms/blob/2.2.1/wtforms/form.py#L305-L308</span></a></p></blockquote>
<p>这里迭代所有的字段属性，然后表单类中是否包含<code>validate_字段名</code>形式的方法。如果有，那么就添加到extra字段里，这个字段被传递到BaseForm类的validate()方法中。在BaseForm类的validate()方法中，WTForms迭代所有字段，并对每个字段调用Field.validate()方法验证字段，继续传入自定义的行内验证方法：</p>
<div>
<pre class="">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
</pre>
</div>
<blockquote><p>源码位置：<a class=" external" href="https://link.zhihu.com/?target=https%3A//github.com/wtforms/wtforms/blob/2.2.1/wtforms/form.py%23L134-L154" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043"><span class="invisible">https://</span><span class="visible">github.com/wtforms/wtfo</span><span class="invisible">rms/blob/2.2.1/wtforms/form.py#L134-L154</span></a></p></blockquote>
<p>而在字段基类Field的validate()方法中，WTForms使用itertool模块提供的chain()函数把你实例化字段类时传入的验证器（<code>self.validators</code>）和自定义行内验证器（<code>extra_validators</code>）连接到一起：</p>
<div>
<pre class="">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)  # 运行验证器
        ...
</pre>
</div>
<blockquote><p>源码位置：<a class=" external" href="https://link.zhihu.com/?target=https%3A//github.com/wtforms/wtforms/blob/2.2.1/wtforms/fields/core.py%23L204-L206" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043"><span class="invisible">https://</span><span class="visible">github.com/wtforms/wtfo</span><span class="invisible">rms/blob/2.2.1/wtforms/fields/core.py#L204-L206</span></a></p></blockquote>
<p>连接起来的所有验证器赋值为chain变量，并传入到<code>self._run_validation_chain(form, chain)</code>进行进一步调用：</p>
<div>
<pre class="">class Field(object):    
    ...
    def _run_validation_chain(self, form, validators):
        for validator in validators:
            try:
                validator(form, self)  # 传入字段类本身作为第二个参数
</pre>
</div>
<blockquote><p>源码位置：<a class=" external" href="https://link.zhihu.com/?target=https%3A//github.com/wtforms/wtforms/blob/2.2.1/wtforms/fields/core.py%23L226" target="_blank" rel="nofollow noopener noreferrer" data-za-detail-view-id="1043"><span class="invisible">https://</span><span class="visible">github.com/wtforms/wtfo</span><span class="invisible">rms/blob/2.2.1/wtforms/fields/core.py#L226</span></a></p></blockquote>
<p>这个方法迭代所有的验证器对字段数据进行验证。关键在于validator(form, self)，可以看到这里传入了第二个参数self，即Field类本身，这也是为什么你可以在自定义验证方法中通过field.data获取当前字段数据。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>https://greyli.com/how-custom-validator-work-in-wtforms/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
