自定义Form组件
一、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 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
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 = 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
二、自定义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): passclass InputText(Widget):
def call(self, *args, **kwargs):</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"><input type='text' name='name'></span><span style="color: rgba(128, 0, 0, 1)">"</span>
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}<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> validate(self,request_data): </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">先找到所有的字段,在执行每一个字段的validate方法</span> flag =<span style="color: rgba(0, 0, 0, 1)"> True </span><span style="color: rgba(0, 0, 255, 1)">for</span> name, field <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> self._fields.items(): input_val </span>= request_data.get(name,<span style="color: rgba(128, 0, 0, 1)">""</span>) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">用户输入的值</span> result= field.validate(input_val) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">每一个字段自己校验</span> <span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">???????????</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,input_val,result) </span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> result: flag </span>=<span style="color: rgba(0, 0, 0, 1)"> False </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: self.data[name] </span>=<span style="color: rgba(0, 0, 0, 1)"> input_val </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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 = ".@."<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> validate(self,val): </span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> re </span><span style="color: rgba(0, 0, 255, 1)">print</span>(re.match(self.reg,val),<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> re.match(self.reg,val): </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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()