一、wtforms源码流程
1、实例化流程分析
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 class16 # 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.default38 39 self.object_data = data40 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__ 方法,返回标签字符串
2、验证流程分析
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 = None13 success = True14 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 = False21 return success22 c. 每个字段进行验证时候23 字段的pre_validate 【预留的扩展】24 字段的_run_validation_chain,对正则和字段的钩子函数进行校验25 字段的post_validate【预留的扩展】
二、自定义Form组件
#!usr/bin/env python# -*- coding:utf-8 -*-from flask import Flask,render_template,request,Markupapp = Flask(__name__,template_folder="templates")app.debug = True# ==============通过这几个类就可以显示了-==============#插件class Widget(object): passclass InputText(Widget): def __call__(self, *args, **kwargs): return ""class TextArea(Widget): def __call__(self, *args, **kwargs): return Markup("")#Formclass 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,selfclass StringField(Field): #每个字段打印的时候都要去执行__str__,所以选择放在基类里面,自己没有就调用父类的 widget = InputText() def validate(self,val): if val: return Trueclass 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()