1、wtforms源碼流程html
一、實例化流程分析python
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__ 方法,返回標籤字符串
二、驗證流程分析flask
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【預留的擴展】
2、自定義Form組件app
#!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()