博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
wtforms Form实例化流程(源码解析)
阅读量:5078 次
发布时间:2019-06-12

本文共 12915 字,大约阅读时间需要 43 分钟。

 
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 '
' % (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 
` 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【预留的扩展】

 

转载于:https://www.cnblogs.com/lianxuebin/p/8664311.html

你可能感兴趣的文章
Window 的引导过程
查看>>
App右上角数字
查看>>
从.NET中委托写法的演变谈开去(上):委托与匿名方法
查看>>
小算法
查看>>
201521123024 《java程序设计》 第12周学习总结
查看>>
新作《ASP.NET MVC 5框架揭秘》正式出版
查看>>
IdentityServer4-用EF配置Client(一)
查看>>
WPF中实现多选ComboBox控件
查看>>
读构建之法第四章第十七章有感
查看>>
Windows Phone开发(4):框架和页 转:http://blog.csdn.net/tcjiaan/article/details/7263146
查看>>
Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八)【转】
查看>>
python asyncio 异步实现mongodb数据转xls文件
查看>>
TestNG入门
查看>>
【ul开发攻略】HTML5/CSS3菜单代码 阴影+发光+圆角
查看>>
[ZJOI2007]棋盘制作 【最大同色矩形】
查看>>
IOS-图片操作集合
查看>>
模板统计LA 4670 Dominating Patterns
查看>>
团队项目开发客户端——登录子系统的设计
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
session如何保存在专门的StateServer服务器中
查看>>