1. 程式人生 > >Flask系列(十)自定義Form組件

Flask系列(十)自定義Form組件

pri 擴展 append BE sam cep default .get pos

一、wtforms源碼流程

1、實例化流程分析

技術分享圖片
# 源碼流程
    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

2、驗證流程分析

技術分享圖片
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

二、自定義From組件

#!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()

Flask系列(十)自定義Form組件