1. 程式人生 > >wtforms Form實例化流程(源碼解析)

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

inter filters ram subclass max 用戶 ant base __str__

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__()方法來實例化

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

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實例實際開始是這樣的(註釋)

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__()方法:

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的process

    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__方法:

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

我們可以看到他返回self(),對象()---->執行當前類的__call__()方法:

 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)執行後的結果

    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【預留的擴展】

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