Flask之WTForms

一:介绍

WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

安装:pip3 install wtforms

1. 用户登录

当用户登录时候,需要对用户提交的用户名和密码进行多种格式校验。如:

用户不能为空;用户长度必须大于6;

密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 from flask import Flask, render_template, request, redirect
 4 from wtforms import Form
 5 from wtforms.fields import core
 6 from wtforms.fields import html5
 7 from wtforms.fields import simple
 8 from wtforms import validators
 9 from wtforms import widgets
10 
11 app = Flask(__name__, template_folder='templates')
12 app.debug = True
13 
14 
15 class LoginForm(Form):
16     name = simple.StringField(
17         label='用户名',
18         validators=[
19             validators.DataRequired(message='用户名不能为空.'),
20             validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
21         ],
22         widget=widgets.TextInput(),
23         render_kw={'class': 'form-control'}
24 
25     )
26     pwd = simple.PasswordField(
27         label='密码',
28         validators=[
29             validators.DataRequired(message='密码不能为空.'),
30             validators.Length(min=8, message='用户名长度必须大于%(min)d'),
31             validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
32                               message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
33 
34         ],
35         widget=widgets.PasswordInput(),
36         render_kw={'class': 'form-control'}
37     )
38 
39 
40 
41 @app.route('/login', methods=['GET', 'POST'])
42 def login():
43     if request.method == 'GET':
44         form = LoginForm()
45         return render_template('login.html', form=form)
46     else:
47         form = LoginForm(formdata=request.form)
48         if form.validate():
49             print('用户提交数据通过格式验证,提交的值为:', form.data)
50         else:
51             print(form.errors)
52         return render_template('login.html', form=form)
53 
54 if __name__ == '__main__':
55     app.run()
56 
57 app.py
View Code
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <h1>登录</h1>
 9 <form method="post">
10     <!--<input type="text" name="name">-->
11     <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
12 
13     <!--<input type="password" name="pwd">-->
14     <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
15     <input type="submit" value="提交">
16 </form>
17 </body>
18 </html>
19 
20 login.html
login.html

2. 用户注册

注册页面需要让用户输入:用户名、密码、密码重复、性别、爱好等。

  1 from flask import Flask, render_template, request, redirect
  2 from wtforms import Form
  3 from wtforms.fields import core
  4 from wtforms.fields import html5
  5 from wtforms.fields import simple
  6 from wtforms import validators
  7 from wtforms import widgets
  8 
  9 app = Flask(__name__, template_folder='templates')
 10 app.debug = True
 11 
 12 
 13 
 14 class RegisterForm(Form):
 15     name = simple.StringField(
 16         label='用户名',
 17         validators=[
 18             validators.DataRequired()
 19         ],
 20         widget=widgets.TextInput(),
 21         render_kw={'class': 'form-control'},
 22         default='alex'
 23     )
 24 
 25     pwd = simple.PasswordField(
 26         label='密码',
 27         validators=[
 28             validators.DataRequired(message='密码不能为空.')
 29         ],
 30         widget=widgets.PasswordInput(),
 31         render_kw={'class': 'form-control'}
 32     )
 33 
 34     pwd_confirm = simple.PasswordField(
 35         label='重复密码',
 36         validators=[
 37             validators.DataRequired(message='重复密码不能为空.'),
 38             validators.EqualTo('pwd', message="两次密码输入不一致")
 39         ],
 40         widget=widgets.PasswordInput(),
 41         render_kw={'class': 'form-control'}
 42     )
 43 
 44     email = html5.EmailField(
 45         label='邮箱',
 46         validators=[
 47             validators.DataRequired(message='邮箱不能为空.'),
 48             validators.Email(message='邮箱格式错误')
 49         ],
 50         widget=widgets.TextInput(input_type='email'),
 51         render_kw={'class': 'form-control'}
 52     )
 53 
 54     gender = core.RadioField(
 55         label='性别',
 56         choices=(
 57             (1, ''),
 58             (2, ''),
 59         ),
 60         coerce=int
 61     )
 62     city = core.SelectField(
 63         label='城市',
 64         choices=(
 65             ('bj', '北京'),
 66             ('sh', '上海'),
 67         )
 68     )
 69 
 70     hobby = core.SelectMultipleField(
 71         label='爱好',
 72         choices=(
 73             (1, '篮球'),
 74             (2, '足球'),
 75         ),
 76         coerce=int
 77     )
 78 
 79     favor = core.SelectMultipleField(
 80         label='喜好',
 81         choices=(
 82             (1, '篮球'),
 83             (2, '足球'),
 84         ),
 85         widget=widgets.ListWidget(prefix_label=False),
 86         option_widget=widgets.CheckboxInput(),
 87         coerce=int,
 88         default=[1, 2]
 89     )
 90 
 91     def __init__(self, *args, **kwargs):
 92         super(RegisterForm, self).__init__(*args, **kwargs)
 93         self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
 94 
 95     def validate_pwd_confirm(self, field):
 96         """
 97         自定义pwd_confirm字段规则,例:与pwd字段是否一致
 98         :param field: 
 99         :return: 
100         """
101         # 最开始初始化时,self.data中已经有所有的值
102 
103         if field.data != self.data['pwd']:
104             # raise validators.ValidationError("密码不一致") # 继续后续验证
105             raise validators.StopValidation("密码不一致")  # 不再继续后续验证
106 
107 
108 @app.route('/register', methods=['GET', 'POST'])
109 def register():
110     if request.method == 'GET':
111         form = RegisterForm(data={'gender': 1})
112         return render_template('register.html', form=form)
113     else:
114         form = RegisterForm(formdata=request.form)
115         if form.validate():
116             print('用户提交数据通过格式验证,提交的值为:', form.data)
117         else:
118             print(form.errors)
119         return render_template('register.html', form=form)
120 
121 
122 
123 if __name__ == '__main__':
124     app.run()
125 
126 app.py
app.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <h1>用户注册</h1>
 9 <form method="post" novalidate style="padding:0  50px">
10     {% for item in form %}
11     <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
12     {% endfor %}
13     <input type="submit" value="提交">
14 </form>
15 </body>
16 </html>
17 
18 register.html
register.html

3. meta

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 from flask import Flask, render_template, request, redirect, session
 4 from wtforms import Form
 5 from wtforms.csrf.core import CSRF
 6 from wtforms.fields import core
 7 from wtforms.fields import html5
 8 from wtforms.fields import simple
 9 from wtforms import validators
10 from wtforms import widgets
11 from hashlib import md5
12 
13 app = Flask(__name__, template_folder='templates')
14 app.debug = True
15 
16 
17 class MyCSRF(CSRF):
18     """
19     Generate a CSRF token based on the user's IP. I am probably not very
20     secure, so don't use me.
21     """
22 
23     def setup_form(self, form):
24         self.csrf_context = form.meta.csrf_context()
25         self.csrf_secret = form.meta.csrf_secret
26         return super(MyCSRF, self).setup_form(form)
27 
28     def generate_csrf_token(self, csrf_token):
29         gid = self.csrf_secret + self.csrf_context
30         token = md5(gid.encode('utf-8')).hexdigest()
31         return token
32 
33     def validate_csrf_token(self, form, field):
34         print(field.data, field.current_token)
35         if field.data != field.current_token:
36             raise ValueError('Invalid CSRF')
37 
38 
39 class TestForm(Form):
40     name = html5.EmailField(label='用户名')
41     pwd = simple.StringField(label='密码')
42 
43     class Meta:
44         # -- CSRF
45         # 是否自动生成CSRF标签
46         csrf = True
47         # 生成CSRF标签name
48         csrf_field_name = 'csrf_token'
49 
50         # 自动生成标签的值,加密用的csrf_secret
51         csrf_secret = 'xxxxxx'
52         # 自动生成标签的值,加密用的csrf_context
53         csrf_context = lambda x: request.url
54         # 生成和比较csrf标签
55         csrf_class = MyCSRF
56 
57         # -- i18n
58         # 是否支持本地化
59         # locales = False
60         locales = ('zh', 'en')
61         # 是否对本地化进行缓存
62         cache_translations = True
63         # 保存本地化缓存信息的字段
64         translations_cache = {}
65 
66 
67 @app.route('/index/', methods=['GET', 'POST'])
68 def index():
69     if request.method == 'GET':
70         form = TestForm()
71     else:
72         form = TestForm(formdata=request.form)
73         if form.validate():
74             print(form)
75     return render_template('index.html', form=form)
76 
77 
78 if __name__ == '__main__':
79     app.run()
View Code

4.实例化流程分析

 1 # 源码流程
 2     1. 执行type的 __call__ 方法,读取字段到静态字段 cls._unbound_fields 中; meta类读取到cls._wtforms_meta中
 3     2. 执行构造方法
 4         
 5         a. 循环cls._unbound_fields中的字段,并执行字段的bind方法,然后将返回值添加到 self._fields[name] 中。
 6             即:
 7                 _fields = {
 8                     name: wtforms.fields.core.StringField(),
 9                 }
10                 
11             PS:由于字段中的__new__方法,实例化时:name = simple.StringField(label='用户名'),创建的是UnboundField(cls, *args, **kwargs),当执行完bind之后,才变成执行 wtforms.fields.core.StringField()
12         
13         b. 循环_fields,为对象设置属性
14             for name, field in iteritems(self._fields):
15                 # Set all the fields to attributes so that they obscure the class
16                 # attributes with the same names.
17                 setattr(self, name, field)
18         c. 执行process,为字段设置默认值:self.process(formdata, obj, data=data, **kwargs)
19             优先级:obj,data,formdata;
20             
21             再循环执行每个字段的process方法,为每个字段设置值:
22             for name, field, in iteritems(self._fields):
23                 if obj is not None and hasattr(obj, name):
24                     field.process(formdata, getattr(obj, name))
25                 elif name in kwargs:
26                     field.process(formdata, kwargs[name])
27                 else:
28                     field.process(formdata)
29             
30             执行每个字段的process方法,为字段的data和字段的raw_data赋值
31             def process(self, formdata, data=unset_value):
32                 self.process_errors = []
33                 if data is unset_value:
34                     try:
35                         data = self.default()
36                     except TypeError:
37                         data = self.default
38         
39                 self.object_data = data
40         
41                 try:
42                     self.process_data(data)
43                 except ValueError as e:
44                     self.process_errors.append(e.args[0])
45         
46                 if formdata:
47                     try:
48                         if self.name in formdata:
49                             self.raw_data = formdata.getlist(self.name)
50                         else:
51                             self.raw_data = []
52                         self.process_formdata(self.raw_data)
53                     except ValueError as e:
54                         self.process_errors.append(e.args[0])
55         
56                 try:
57                     for filter in self.filters:
58                         self.data = filter(self.data)
59                 except ValueError as e:
60                     self.process_errors.append(e.args[0])
61                 
62         d. 页面上执行print(form.name) 时,打印标签
63             
64             因为执行了:
65                 字段的 __str__ 方法
66                 字符的 __call__ 方法
67                 self.meta.render_field(self, kwargs)
68                     def render_field(self, field, render_kw):
69                         other_kw = getattr(field, 'render_kw', None)
70                         if other_kw is not None:
71                             render_kw = dict(other_kw, **render_kw)
72                         return field.widget(field, **render_kw)
73                 执行字段的插件对象的 __call__ 方法,返回标签字符串
View Code

5.验证流程分析

 1 a. 执行form的validate方法,获取钩子方法
 2             def validate(self):
 3                 extra = {}
 4                 for name in self._fields:
 5                     inline = getattr(self.__class__, 'validate_%s' % name, None)
 6                     if inline is not None:
 7                         extra[name] = [inline]
 8         
 9                 return super(Form, self).validate(extra)
10         b. 循环每一个字段,执行字段的 validate 方法进行校验(参数传递了钩子函数)
11             def validate(self, extra_validators=None):
12                 self._errors = None
13                 success = True
14                 for name, field in iteritems(self._fields):
15                     if extra_validators is not None and name in extra_validators:
16                         extra = extra_validators[name]
17                     else:
18                         extra = tuple()
19                     if not field.validate(self, extra):
20                         success = False
21                 return success
22         c. 每个字段进行验证时候
23             字段的pre_validate 【预留的扩展】
24             字段的_run_validation_chain,对正则和字段的钩子函数进行校验
25             字段的post_validate【预留的扩展】
View Code

 6.详细分析

  1 当程序从上往下进行执行加载的时候到达class LoginForm(Form):的时候会先加载类调用里面的__init__方法,那么我们需要明确LoginForm这个类是由谁创建的,通过分析之后,LoginForm这个类继承Form类,class Form(with_metaclass(FormMeta, BaseForm)):
  2 def with_metaclass(meta, base=object):
  3 # meta=FormMeta, BaseForm
  4 # base=BaseForm
  5     return meta("NewBase", (base,), {})
  6 所以:NewBase = FormMeta("NewBase", (BaseForm,), {}),NewBase 这个类是由FormMeta这个类创建的并且继承BaseForm这个类
  7 class NewBase (BaseForm,metaclass=FormMeta)
  8 因此得出:class Form(NewBase)
  9 1.由于 metaclass=FormMeta,所以LoginForm是由FormMeta创建
 10 2.那么创建的时候就会执行FormMeta类的__init__方法
 11 def __init__(cls, name, bases, attrs):
 12     type.__init__(cls, name, bases, attrs)
 13     cls._unbound_fields = None
 14     cls._wtforms_meta = None
 15 执行 FormMeta.__init__就会得到
 16 # LoginForm._unbound_fields = None
 17 # LoginForm._wtforms_meta = None
 18 
 19 3.解释字段:simple.StringField(...)进行实例化执行里面的__new__方法和__init__方法
 20 # name = simple.StringField(...)
 21 # pwd = simple.PasswordField(...)
 22 class StringField(Field):-- 23 def __new__(cls, *args, **kwargs):
 24     if '_form' in kwargs and '_name' in kwargs:
 25         return super(Field, cls).__new__(cls)
 26     else:
 27         return UnboundField(cls, *args, **kwargs)
 28 因为在StringField传过来的参数里面没有_form或者_name,所以直接返回UnboundField对象将StringField类以及里面的参数全部传过去,那么这些执行好了之后
 29  结果:
 30 # LoginForm.name = UnboundField(simple.StringField,StringField的所有参数)
 31 # LoginForm.pwd = UnboundField(simple.PasswordField,PasswordField的所有参数)
 32 此时我们可以查看一下LoginForm里面到底有些什么
 33 print(LoginForm.__dict__),打印出该类里面所有的对象
 34 LoginForm ={
 35    '__module__': '__main__', 
 36    'name': <1 UnboundField(StringField, (), {'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>, 
 37    'pwd': <2 UnboundField(PasswordField, (), {'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
 38    '__doc__': None, 
 39    '_unbound_fields': None, 
 40    '_wtforms_meta': None
 41 
 42 补充:__new__用于生成对象
 43 class Foo(object):
 44     def __init__(self):
 45         pass
 46     def __new__(cls, *args, **kwargs):
 47         """
 48         用于生成对象
 49         :param args:
 50         :param kwargs:
 51         :return:
 52         """
 53         return super(Foo,cls).__new__(cls, *args, **kwargs)
 54         # return "666"
 55 obj = Foo()
 56 print(obj)
 57 
 58 我们再看_unbound_fields里面做了什么,其实就是对字段进行排序
 59 def __init__(self, field_class, *args, **kwargs):
 60     UnboundField.creation_counter += 1
 61     self.field_class = field_class
 62     self.args = args
 63     self.kwargs = kwargs
 64     self.creation_counter = UnboundField.creation_counter
 65 经过这些之后就会在LoginForm 里面形成有序的顺序
 66 
 67 加载完类和字段之后就会执行函数def login():,首先会# 实例LoginForm
 68 1.因为LoginForm是由FormMeta创建的,那么实例化的时候首先回调用、执行FormMeta的__call__方法
 69 def __call__(cls, *args, **kwargs):
 70     """
 71     Construct a new `Form` instance.
 72 
 73     Creates the `_unbound_fields` list and the internal `_wtforms_meta`
 74     subclass of the class Meta in order to allow a proper inheritance
 75     hierarchy.
 76     """
 77     if cls._unbound_fields is None:
 78         fields = []
 79         for name in dir(cls):
 80             if not name.startswith('_'):
 81                 unbound_field = getattr(cls, name)
 82                 if hasattr(unbound_field, '_formfield'):
 83                     fields.append((name, unbound_field))
 84         # We keep the name as the second element of the sort
 85         # to ensure a stable sort.
 86         fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
 87         cls._unbound_fields = fields
 88 
 89     # Create a subclass of the 'class Meta' using all the ancestors.
 90     if cls._wtforms_meta is None:
 91         bases = []
 92         for mro_class in cls.__mro__:
 93             if 'Meta' in mro_class.__dict__:
 94                 bases.append(mro_class.Meta)
 95         cls._wtforms_meta = type('Meta', tuple(bases), {})
 96     return type.__call__(cls, *args, **kwargs)
 97 
 98 cls._unbound_fields开始为空,创建一个空列表,for name in dir(cls):循环里面的所有字典的key 如果没有if not name.startswith('_'):开头,getattr(cls, name)通过类掉方法,LoginForm.name得到对应的值,<1 UnboundField(StringField, (), {'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>, 然后fields.append((name, unbound_field))里面写值,
 99 经过这个之后
100 '_unbound_fields': [
101     (name, UnboundField对象(1,simple.StringField,参数)),
102     (pwd, UnboundField对象(2,simple.PasswordField,参数)),
103 ],
104  if cls._wtforms_meta is None:
105         bases = []
106         for mro_class in cls.__mro__:
107             if 'Meta' in mro_class.__dict__:
108                 bases.append(mro_class.Meta)
109         cls._wtforms_meta = type('Meta', tuple(bases), {})
110     return type.__call__(cls, *args, **kwargs)
111 
112 mro_class in cls.__mro__:这里面其实就是对类的继承的排序:
113 class F4(object):
114     pass
115 
116 class F3(F4):
117     pass
118 
119 class F2_5(object):
120     pass
121 
122 class F2(F2_5):
123     pass
124 
125 class F1(F2,F3):
126     pass
127 
128 print(F1.__mro__)
129 
130 F1先继承F2在-》F2_5-》object,然后再F3—》F4--》object
131 
132 执行这个函数之后就会cls._wtforms_meta = type('Meta', tuple(bases), {})
133 Meta = DefaultMeta
134 class Meta(DefaultMeta,classmata=type)
135 class Meta(DefaultMeta,):
136     pass
137 
138 LoginForm ={
139     '__module__': '__main__',
140     'name': <2 UnboundField(StringField, (), {'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>,
141     'pwd': <1 UnboundField(PasswordField, (), {'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>,
142     '__doc__': None,
143     '_unbound_fields': [
144         (name, UnboundField对象(1,simple.StringField,参数)),
145         (pwd, UnboundField对象(2,simple.PasswordField,参数)),
146     ],
147     '_wtforms_meta': Meta
148 
149 2. 执行LoginForm的__new__方法
150     pass
151 
152 3. 执行LoginForm的__init__方法
153 def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
154     meta_obj = self._wtforms_meta()
155     if meta is not None and isinstance(meta, dict):
156         meta_obj.update_values(meta)
157     super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
158 
159     for name, field in iteritems(self._fields):
160         # Set all the fields to attributes so that they obscure the class
161         # attributes with the same names.
162         setattr(self, name, field)
163 
164  LoginForm ={
165         '__module__': '__main__',
166         'name': <2 UnboundField(StringField, (), {'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>,
167         'pwd': <1 UnboundField(PasswordField, (), {'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>,
168         '__doc__': None,
169         '_unbound_fields': [
170             (name, UnboundField对象(1,simple.StringField,参数)),
171             (pwd, UnboundField对象(2,simple.PasswordField,参数)),
172         ],
173         '_wtforms_meta': Meta
174 
175     }
176 form = {
177     _fields: {
178             name: StringField对象(),
179             pwd: PasswordField对象(),
180         }
181     name:  StringField对象(widget=widgets.TextInput()),
182     pwd:  PasswordField对象(widget=widgets.PasswordInput())
183     }
184 
185 所以在这里最后就能形成form.name
186 打印print(form.name)-》打印StringField对象()首先会调用StringField对象.__str__
187 # form._fields['name']
188 # form.name = StringField对象()
189 """
190 1. StringField对象.__str__
191 2. StringField对象.__call__
192 3. meta.render_field(StringField对象,)
193 4. StringField对象.widget(field, **render_kw)
194 5. 插件.__call__()
195 """
196 print(form.name)  #
197 """
198 0. Form.__iter__: 返回所有字段对象
199     1. StringField对象.__str__
200     2. StringField对象.__call__
201     3. meta.render_field(StringField对象,)
202     4. StringField对象.widget(field, **render_kw)
203     5. 插件.__call__()
204 """
205 form = {
206    _fields: {
207            name: StringField对象(data=你输入的用户名),
208            pwd: PasswordField对象(pwd=你输入的密码),
209        }
210    name:  StringField对象(widget=widgets.TextInput(data=你输入的用户名)),
211    pwd:  PasswordField对象(widget=widgets.PasswordInput(pwd=你输入的密码))
212 
213    }
214 # 请求发过来的值
215 form = LoginForm(formdata=request.form)  # 值.getlist('name')
216 
217 # 实例:编辑
218 # # 从数据库对象
219 # form = LoginForm(obj='值') # 值.name/值.pwd
220 #
221 # # 字典 {}
222 # form = LoginForm(data=request.form) # 值['name']
223 
224 
225 # 1. 循环所有的字段
226 # 2. 获取每个字段的钩子函数
227 # 3. 为每个字段执行他的验证流程 字段.validate(钩子函数+内置验证规则)
228 if form.validate():
229     print(form.data)
230 else:
231     print(form.errors)
232 
233 def validate(self):
234     """
235     Validates the form by calling `validate` on each field, passing any
236     extra `Form.validate_<fieldname>` validators to the field validator.
237     """
238 # extra = {}拿到钩子函数
239     extra = {}
240     for name in self._fields:
241 #name:  StringField对象(widget=widgets.TextInput(data=你输入的用户名)),
242 #pwd:  PasswordField对象(widget=widgets.PasswordInput(pwd=你输入的密码))
243         inline = getattr(self.__class__, 'validate_%s' % name, None)
244         if inline is not None:
245             extra[name] = [inline]
246     return super(Form, self).validate(extra)
247 
248 # extra = {}拿到钩子函数
249 def validate(self, extra_validators=None):
250     self._errors = None
251     success = True
252 #name:  StringField对象(widget=widgets.TextInput(data=你输入的用户名)),
253 #pwd:  PasswordField对象(widget=widgets.PasswordInput(pwd=你输入的密码))
254     for name, field in iteritems(self._fields):
255         if extra_validators is not None and name in extra_validators:
256             extra = extra_validators[name]
257         else:
258             extra = tuple()
259         if not field.validate(self, extra):
260             success = False
261     return success
262 
263 def validate(self, form, extra_validators=tuple()):
264     # Call pre_validate
265     try:
266         self.pre_validate(form)
267     except StopValidation as e:
268         if e.args and e.args[0]:
269             self.errors.append(e.args[0])
270         stop_validation = True
271     except ValueError as e:
272         self.errors.append(e.args[0])
273 
274     # Run validators
275     if not stop_validation:
276         chain = itertools.chain(self.validators, extra_validators)
277         stop_validation = self._run_validation_chain(form, chain)
278 
279     # Call post_validate
280     try:
281         self.post_validate(form, stop_validation)
282     except ValueError as e:
283         self.errors.append(e.args[0])
284 
285     return len(self.errors) == 0
286 
287 def _run_validation_chain(self, form, validators):
288     """
289     Run a validation chain, stopping if any validator raises StopValidation.
290 
291     :param form: The Form instance this field belongs to.
292     :param validators: a sequence or iterable of validator callables.
293     :return: True if validation was stopped, False otherwise.
294     """
295     for validator in validators:
296         try:
297             validator(form, self)
298         except StopValidation as e:
299             if e.args and e.args[0]:
300                 self.errors.append(e.args[0])
301             return True
302         except ValueError as e:
303             self.errors.append(e.args[0])
304     return False
View Code

 

posted @ 2018-11-23 16:14  chenjunkan  阅读(207)  评论(0编辑  收藏  举报