wtforms Form實例化流程(源碼解析)

class LoginForm(Form): #首先執行後獲得的結果是UnboundField()對象
    name=simple.StringField( label='用戶名', validators=[ validators.DataRequired(message='用戶名不能爲空'), ], widget=widgets.TextInput(), render_kw={'class': 'form-control'} ) pwd=simple.StringField( label='密碼', validators=[ validators.DataRequired(message='密碼不能爲空'), ], widget=widgets.TextInput(), render_kw={'class': 'form-control'} ) @user.route('/login',methods=['GET','POST']) def login(): if request.method=='GET': form=LoginForm() print(form) return render_template('login.html',form=form) else: form=LoginForm(request.form) if form.validate():

1.執行Field中的__new__方法

咱們還沒執行到form=LoginForm()時,LoginForm裏面全部的字段都已經執行加載完了,裏面的字段的值都是Field實例化而來,而實例化一個類,先執行該類的__new__方法來建立這個類,而後調用__init__()方法來實例化html

,本類中沒有就去父類中找,前端

Field中的__new__()方法後端

def __new__(cls, *args, **kwargs): if '_form' in kwargs and '_name' in kwargs: return super(Field, cls).__new__(cls) else: return UnboundField(cls, *args, **kwargs)

能夠知道開始的時候全部的Field對象都是UnboundField()對象,咱們所寫的Filed實例實際開始是這樣的(註釋)app

class LoginForm(Form): # name = UnboundField(StringField, *args, **kwargs) creation_counter=1
    name = simple.StringField( label='用戶名', validators=[ validators.DataRequired(message='用戶名不能爲空.'), validators.Length(min=6, max=18, message='用戶名長度必須大於%(min)d且小於%(max)d') ], widget=widgets.TextInput(), render_kw={'class': 'form-control'} ) # pwd = UnboundField(PasswordField, *args, **kwargs) creation_counter=2
    pwd = simple.PasswordField( label='密碼', validators=[ validators.DataRequired(message='密碼不能爲空.'), validators.Length(min=8, message='用戶名長度必須大於%(min)d'), validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}", message='密碼至少8個字符,至少1個大寫字母,1個小寫字母,1個數字和1個特殊字符') ], widget=widgets.PasswordInput(), render_kw={'class': 'form-control'} )

2.執行FormMeta的__call__()方法,讀取字段到靜態字段 cls._unbound_fields 中; meta類讀取到cls._wtforms_meta中

若是一個類有metaclass,那麼該類建立的時候會執行她的metaclass類中的__init__()方法,實例話這個類回執行 metaclass中的__call__()方法:ide

class Form(with_metaclass(FormMeta, BaseForm)):

FormMeta的__call__()方法

def __call__(cls, *args, **kwargs): """ Construct a new `Form` instance. Creates the `_unbound_fields` list and the internal `_wtforms_meta` subclass of the class Meta in order to allow a proper inheritance hierarchy. """
        if cls._unbound_fields is None: fields = [] #當前類全部的屬性
            for name in dir(cls): if not name.startswith('_'): #獲得UnboundField()對象
                    unbound_field = getattr(cls, name)
#UnboundField()對象默認_formfield爲True
if hasattr(unbound_field, '_formfield'): fields.append((name, unbound_field)) # We keep the name as the second element of the sort # to ensure a stable sort. #根據UnboundField()對象的.creation_counter進行排序 fields.sort(key=lambda x: (x[1].creation_counter, x[0])) #fields=[('name',UnboundField()),('pwd',UnboundField())] cls._unbound_fields = fields # Create a subclass of the 'class Meta' using all the ancestors. if cls._wtforms_meta is None: bases = [] #__mro__表明該類的繼承關係 for mro_class in cls.__mro__: if 'Meta' in mro_class.__dict__: bases.append(mro_class.Meta) cls._wtforms_meta = type('Meta', tuple(bases), {}) return type.__call__(cls, *args, **kwargs)

3.執行Form類的構造方法:

def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs): """ :param formdata: Used to pass data coming from the enduser, usually `request.POST` or equivalent. formdata should be some sort of request-data wrapper which can get multiple parameters from the form input, and values are unicode strings, e.g. a Werkzeug/Django/WebOb MultiDict :param obj: If `formdata` is empty or not provided, this object is checked for attributes matching form field names, which will be used for field values. :param prefix: If provided, all fields will have their name prefixed with the value. :param data: Accept a dictionary of data. This is only used if `formdata` and `obj` are not present. :param meta: If provided, this is a dictionary of values to override attributes on this form's meta instance. :param `**kwargs`: If `formdata` is empty or not provided and `obj` does not contain an attribute named the same as a field, form will assign the value of a matching keyword argument to the field, if one exists. """ meta_obj = self._wtforms_meta() if meta is not None and isinstance(meta, dict): meta_obj.update_values(meta) super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix) 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) self.process(formdata, obj, data=data, **kwargs)

 

super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)

a.先執行 baseForm中的__init__()

def __init__(self, fields, prefix='', meta=DefaultMeta()): """ :param fields: A dict or sequence of 2-tuples of partially-constructed fields. :param prefix: If provided, all fields will have their name prefixed with the value. :param meta: A meta instance which is used for configuration and customization of WTForms behaviors. """
        if prefix and prefix[-1] not in '-_;:/.': prefix += '-' self.meta = meta self._prefix = prefix self._errors = None self._fields = OrderedDict() if hasattr(fields, 'items'): fields = fields.items() translations = self._get_translations() extra_fields = [] if meta.csrf: self._csrf = meta.build_csrf(self) extra_fields.extend(self._csrf.setup_form(self)) for name, unbound_field in itertools.chain(fields, extra_fields): options = dict(name=name, prefix=prefix, translations=translations) field = meta.bind_field(self, unbound_field, options) self._fields[name] = field
#將fields和extra_fields連接起來
        for name, unbound_field in itertools.chain(fields, extra_fields): options = dict(name=name, prefix=prefix, translations=translations) field = meta.bind_field(self, unbound_field, options) self._fields[name] = field

b.執行UnboundField中的bind()方法:

class UnboundField(object): _formfield = True creation_counter = 0 def __init__(self, field_class, *args, **kwargs): UnboundField.creation_counter += 1 self.field_class = field_class self.args = args self.kwargs = kwargs self.creation_counter = UnboundField.creation_counter def bind(self, form, name, prefix='', translations=None, **kwargs): kw = dict( self.kwargs, _form=form, _prefix=prefix, _name=name, _translations=translations, **kwargs ) return self.field_class(*self.args, **kw) def __repr__(self): return '<UnboundField(%s, %r, %r)>' % (self.field_class.__name__, self.args, self.kwargs)

在bind方法中咱們能夠看到,因爲字段中的__new__方法,實例化時:name = simple.StringField(label='用戶名'),建立的是UnboundField(cls, *args, **kwargs),當執行完bind以後,就變成執行 wtforms.fields.core.StringField(),函數

c.再回到BaseForm中的__init__()中,將返回值添加到 self._fields[name] 中,既:

_fields = { name: wtforms.fields.core.StringField(), }

d.執行玩BaseForm的__init__()後,在繼續回到Form的構造方法中循環_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) 

e.執行process,爲字段設置默認值:self.process(formdata, obj, data=data, **kwargs),再循環執行每一個字段的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)

f.執行每一個字段的process方法,爲字段的data和字段的raw_data賦值

Field的processpost

def process(self, formdata, data=unset_value): """ Process incoming data, calling process_data, process_formdata as needed, and run filters. If `data` is not provided, process_data will be called on the field's default. Field subclasses usually won't override this, instead overriding the process_formdata and process_data methods. Only override this for special advanced processing, such as when a field encapsulates many inputs. """ 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])

3. 頁面上執行print(form.name) 時,打印標籤,流程以下(參考自定義form組件很容易理解)

咱們在前端和後端上打印的name和pwd實際上是一個Filed的實例,至關於一個實例對象,咱們知道直接print一個對象的時候,會調用該類的__str__方法,因此咱們查看Field的__str__方法:ui

def __str__(self): """ Returns a HTML representation of the field. For more powerful rendering, see the `__call__` method. """
        return self()

咱們能夠看到他返回self(),對象()---->執行當前類的__call__()方法:this

def __call__(self, **kwargs): """ Render this field as HTML, using keyword args as additional attributes. This delegates rendering to :meth:`meta.render_field <wtforms.meta.DefaultMeta.render_field>` whose default behavior is to call the field's widget, passing any keyword arguments from this call along to the widget. In all of the WTForms HTML widgets, keyword arguments are turned to HTML attributes, though in theory a widget is free to do anything it wants with the supplied keyword arguments, and widgets don't have to even do anything related to HTML. """
        return self.meta.render_field(self, kwargs)

最終返回值是meta.render_field(self, kwargs)執行後的結果spa

def render_field(self, field, render_kw): """ render_field allows customization of how widget rendering is done. The default implementation calls ``field.widget(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)

調用插件返回對應的Html頁面代碼

 4.驗證流程

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【預留的擴展】
相關文章
相關標籤/搜索