实例化流程分析
# 源码流程
1. 执行 type 的 __call__ 方法, 读取字段到静态字段 cls._unbound_fields 中; meta 类读取到 cls._wtforms_meta 中
2. 执行构造方法
a. 循环 cls._unbound_fields 中的字段, 并执行字段的 bind 方法, 然后将返回值添加到 self._fields[name] 中.
即:
- _fields = {
- name: wtforms.fields.core.StringField(),
- }
PS: 由于字段中的__new__方法, 实例化时: name = simple.StringField(label='用户名'), 创建的是 UnboundField(cls, *args, **kwargs), 当执行完 bind 之后, 才变成执行 wtforms.fields.core.StringField()
b. 循环_fields, 为对象设置属性
- for name, field in iteritems(self._fields):
- # Set all the fields to attributes so that they obscure the class
- # attributes with the same names.
- setattr(self, name, field)
c. 执行 process, 为字段设置默认值: self.process(formdata, obj, data=data, **kwargs)
优先级: obj,data,formdata;
再循环执行每个字段的 process 方法, 为每个字段设置值:
- for name, field, in iteritems(self._fields):
- if obj is not None and hasattr(obj, name):
- field.process(formdata, getattr(obj, name))
- elif name in kwargs:
- field.process(formdata, kwargs[name])
- else:
- field.process(formdata)
执行每个字段的 process 方法, 为字段的 data 和字段的 raw_data 赋值
- def process(self, formdata, data=unset_value):
- self.process_errors = []
- if data is unset_value:
- try:
- data = self.default()
- except TypeError:
- data = self.default
- self.object_data = data
- try:
- self.process_data(data)
- except ValueError as e:
- self.process_errors.append(e.args[0])
- if formdata:
- try:
- if self.name in formdata:
- self.raw_data = formdata.getlist(self.name)
- else:
- self.raw_data = []
- self.process_formdata(self.raw_data)
- except ValueError as e:
- self.process_errors.append(e.args[0])
- try:
- for filter in self.filters:
- self.data = filter(self.data)
- except ValueError as e:
- self.process_errors.append(e.args[0])
d. 页面上执行 print(form.name) 时, 打印标签
因为执行了:
字段的 __str__ 方法
字符的 __call__ 方法
- self.meta.render_field(self, kwargs)
- def render_field(self, field, render_kw):
- other_kw = getattr(field, 'render_kw', None)
- if other_kw is not None:
- render_kw = dict(other_kw, **render_kw)
- return field.widget(field, **render_kw)
执行字段的插件对象的 __call__ 方法, 返回标签字符串
View Code
验证流程分析
a. 执行 form 的 validate 方法, 获取钩子方法
- 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)
b. 循环每一个字段, 执行字段的 validate 方法进行校验 (参数传递了钩子函数)
- 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
c. 每个字段进行验证时候
字段的 pre_validate [预留的扩展]
字段的_run_validation_chain, 对正则和字段的钩子函数进行校验
字段的 post_validate[预留的扩展]
View Code
自定义 Form 组件
- #!usr/bin/env python
- # -*- coding:utf-8 -*-
- from flask import Flask,render_template,request,Markup
- App = Flask(__name__,template_folder="templates")
- App.debug = True
- # ============== 通过这几个类就可以显示了 -==============
- # 插件
- class Widget(object):
- pass
- class InputText(Widget):
- def __call__(self, *args, **kwargs):
- return "<input type='text'name='name'>"
- class TextArea(Widget):
- def __call__(self, *args, **kwargs):
- return Markup("<textarea name='email'></textarea>")
- #Form
- class BaseForm(object):
- def __init__(self):
- #获取当前所有的字段
- _fields = {}
- for name, field in self.__class__.__dict__.items():
- if isinstance(field, Field): # 筛选出字段是 name 和 emailDe
- _fields[name] = field
- self._fields = _fields
- self.data = {}
- # print(_fields) # {'name': 111, 'email': 222}
- def validate(self,request_data):
- #先找到所有的字段, 在执行每一个字段的 validate 方法
- flag = True
- for name, field in self._fields.items():
- input_val = request_data.get(name,"") #用户输入的值
- result= field.validate(input_val) #每一个字段自己校验
- print("???????????",input_val,result)
- if not result:
- flag = False
- else:
- self.data[name] = input_val
- return flag
- # 字段
- class Field(object):
- '''所有类的基类'''
- def __str__(self): #python 中的静态字段通过类能找到, 通过对象也能找到
- return Markup(self.widget()) #self 就是 StringField,self
- class StringField(Field): #每个字段打印的时候都要去执行__str__, 所以选择放在基类里面, 自己没有就调用父类的
- widget = InputText()
- def validate(self,val):
- if val:
- return True
- class EmaliField(Field):
- widget = TextArea()
- reg = ".*@.*"
- def validate(self,val):
- import re
- print(re.match(self.reg,val),"************")
- if re.match(self.reg,val):
- return True
- # =============== 使用 ===============
- class LoginForm(BaseForm):
- name = StringField()
- email = EmaliField()
- @App.route('/index', methods=["GET","POST"])
- def index():
- form = LoginForm()
- ret = form.validate(request.form)
- print("验证成功",ret)
- print("验证成功的值",form.data)
- # print(form.name)
- # print(form.email)
- return render_template("index.html",form=form)
- if __name__ == '__main__':
- App.run()
来源: http://www.bubuko.com/infodetail-2973733.html