zoukankan      html  css  js  c++  java
  • flask 源码专题(四):wtforms Form实例化流程以及csrf验证

    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实例化而来

    查看上述 LoginForm 中的 name 字段,可以看到它的值是通过 StringField 类实例化返回的对象。查看 StringField 类,会发现它的整个继承体系中都没有指定 metaclass ,所以实例化时真正返回的对象是由它的 __new__ ,它的 __new__ 方法继承自 wtforms.fields.core.Field :

     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-zd$@$!%*?&]{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中

    如果一个A类有metaclass,那么该A类创建的时候会执行她的metaclass类中的__init__()方法,实例话这个A类回执行 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)#baseForm中的__init__()
    
            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)#在继续回到Form的构造方法中循环_fields,为对象设置属性
            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)#从中执行UnboundField中的bind()方法:
                self._fields[name] = field #将上一行代码返回值添加到 self._fields[name] 中
      #将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])

     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【预留的扩展】
  • 相关阅读:
    asp.net 验证正则表达式
    c语言编程题
    使用Code First创建数据模型
    c语言知识点
    rabbitmq 简单应用
    influxdb(二)
    influxdb(一)
    K8S 日志收集(六):kibana 安装
    K8S 日志收集(五):head插件安装
    K8S 日志收集(四):logstash 安装
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12636407.html
Copyright © 2011-2022 走看看