内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

flask-wtforms组件源码解析

2018-01-11 16:43 出处:清屏网 人气: 评论(0

使用wtforms

class LoginForm(Form):
    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=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():

知识储备:

对象是类创建的,实例化对象时自动执行类的 __new____init__ 方法,对象()执行类的 __call__ 方法

类是 type 创建的,创建类的时候自动执行 type__init__ ,类() 执行 type__call__ 方法(封装了类的 __new____init__ 方法)

源码流程分析:

1.创建LoginForm时,执行其元类的 __init____call__ 方法

class LoginForm(Form):
    pass
    
class Form(with_metaclass(FormMeta, BaseForm)):
    # Form的元类是FormMeta,基类是BaseForm
    pass

"""
等价于
class Form(BaseForm,metaclass=FormMeta):
    pass
"""

def with_metaclass(meta, base=object):
    """
    FormMeta("NewBase", (BaseForm,), {})
    """
    return meta("NewBase", (base,), {})
class FormMeta(type):
    def __init__(cls, name, bases, attrs):
        """
        此时cls是LoginForm
        __init__方法旨在将_unbound_fields和_wtforms_meta字段封装到LoginForm中
        
        class LoginForm(Form):
            _unbound_fields = None
            _wtforms_meta = None
        """
        type.__init__(cls, name, bases, attrs)
        cls._unbound_fields = None
        cls._wtforms_meta = None
        
    def __call__(cls, *args, **kwargs):
        """
        实例化一个LoginForm类对象
        1.填充_unbound_fields属性
        遍历LoginForm的属性,排除掉_开头的字段
        将其他字段排序后存放到_unbound_fields属性中  
        _unbound_fields = [(name, unbound_field),(name, unbound_field),(name, unbound_field)]
        2.填充_wtforms_meta属性
        将LoginForm和它所继承的所有类中的Meta属性抽离出来,组成一个列表bases
        动态创建一个Meta类,继承自bases
        _unbound_fields = Meta
        """
        if cls._unbound_fields is None:
            fields = []
            #当前类所有的属性
            for name in dir(cls):
                if not name.startswith('_'):  # 排除掉系统自定义的属性
                    unbound_field = getattr(cls, name)  # 得到UnboundField()对象
                    if hasattr(unbound_field, '_formfield'):
                        fields.append((name, unbound_field))
            # 根据UnboundField()对象的.creation_counter进行排序
            fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
            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)

2.实例化字段对象

我们还没执行到form=LoginForm()时,LoginForm里面所有的字段都已经执行加载完了,里面的字段的值都是Field实例化而来。

执行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
    pwd = UnboundField(PasswordField, *args, **kwargs) # creation_counter=2

执行Form类的 __init__ 方法:

def __init__(self, label=None, validators=None, filters=tuple(),
             description='', id=None, default=None, widget=None,
             render_kw=None, _form=None, _name=None, _prefix='',
             _translations=None, _meta=None):
             
    if _translations is not None:
        self._translations = _translations

    if _meta is not None:
        self.meta = _meta
    elif _form is not None:
        self.meta = _form.meta
    else:
        raise TypeError("Must provide one of _form or _meta")

    self.default = default
    self.description = description
    self.render_kw = render_kw
    self.filters = filters
    self.flags = Flags()
    self.name = _prefix + _name
    self.short_name = _name
    self.type = type(self).__name__
    self.validators = validators or list(self.validators)

    self.id = id or self.name
    self.label = Label(self.id, label if label is not None else self.gettext(_name.replace('_', ' ').title()))

    if widget is not None:
        self.widget = widget
    # 字段对象的验证规则
    for v in itertools.chain(self.validators, [self.widget]):
        flags = getattr(v, 'field_flags', ())
        for f in flags:
            setattr(self.flags, f, True)
super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
  1. 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

4.执行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(),

5.再回到BaseForm中的 __init__ 中,将返回值添加到 self._fields[name] 中,既:

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

6.执行完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)

7.执行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)

8.执行每个字段的process方法,为字段的data和字段的raw_data赋值

Field的process

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])

页面上执行print(form.name) 时,打印标签,流程如下(参考自定义form组件很容易理解)

我们在前端和后端上打印的name和pwd其实是一个Filed的实例,相当于一个实例对象,我们知道直接print一个对象的时候,会调用该类的 __str__ 方法,所以我们查看Field的 __str__ 方法:

def __str__(self):
    return self()

我们可以看到他返回self(),对象()---->执行当前类的 __call__ 方法:

def __call__(self, **kwargs):
    return self.meta.render_field(self, kwargs)

最终返回值是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)

调用插件返回对应的Html页面代码

9.验证流程

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【预留的扩展】
分享给小伙伴们:
本文标签: flaskwtforms

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号