Flask之WTForms
一:介绍
WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。
安装:pip3 install wtforms
1. 用户登录
当用户登录时候,需要对用户提交的用户名和密码进行多种格式校验。如:
用户不能为空;用户长度必须大于6;
密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
2. 用户注册
注册页面需要让用户输入:用户名、密码、密码重复、性别、爱好等。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
3. meta
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()
4.实例化流程分析
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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__ 方法,返回标签字符串
5.验证流程分析
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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【预留的扩展】
6.详细分析
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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