zoukankan      html  css  js  c++  java
  • Flask--源码解读之 wtforms 执行流程

     首先我们看下wtforms的创建

    from wtforms.form import Form
    #引入Form元素父类
    from wtforms import StringField
    #引入Form验证父类
    from wtforms.validators import DataRequired
    
    class LoginForm(Form):
        name = StringField(
            label = '姓名',
            validators=[
                DataRequired('不能为空!')
            ],
            render_kw={
                'placeholder':'请输入姓名!'
            }
        )
    

    from wtforms.form import Form

    刚开始程序启动时,首先加载compat.py文件,执行:

     def with_metaclass(meta,base=object):
        #返回一个类NewBase并且继承BaseForm,类似于:
        #class NewBase(BaseForm):
        #       pass
        return meta('NewBase’,(base,),{})

    接着加载了form.py文件,包括BaseForm,FormMeta和Form:

    class Form(with_metaclass(FormMeta,BaseForm)):
            pass

    然后加载Form时执行了with_metaclass(FormMeta,BaseForm),返回meta(‘NewBase’,(base,){}时执行:

    class FormMeta(type):
        def __init__(cls, name, bases, attrs):
            #这里会执行type.__init__(),初始化类NewBase类,
            #现在的cls为NewBase
           type.__init__(cls, name, bases, attrs)
           #给NewBase设置属性:初始值为空
           cls._unbound_fields = None
           cls._wtforms_meta = None
    NewBase类初始化完毕后加载Form类,Form类加载到内存后,又会调用class FormMeta(type):中的方法,进行Form类的初始化,然后开始加载LoginForm:
    class LoginForm(Form):
        name = StringField(
            label='姓名',
            validators=[
                DataRequired('不能为空!')
            ],
            render_kw={
                'placeholder': '请输入姓名!'
            }
    
        )   
    这时所有需要的类加载完毕,开始发送get请求初始化LoginForm:
    @home.route('/')
    def index():
        form = LoginForm()
        return render_template('login.html', form=form)
    那么这是发生了什么呢?
    #这时会执行FormMeta的call方法
    class FormMeta(type):
        def __call__(cls, *args, **kwargs):
            if cls._unbound_fields is None:
            #判断如果_unbound_fields为空
                  fields = []
                  for name in dir(cls):
                      #或者该类的所有的属性
                      if not name.startswith('_'):
                          #遍历所有的名字,如果不是以下滑线开头,获取到给unbound_field赋值:
                          unbound_field = getattr(cls, name)
                          if hasattr(unbound_field, '_formfield'):
                              #筛选真正需要的LoginForm字段
                              fields.append((name, unbound_field))
                   #对要渲染的字段进行排序,所以字段前端字段的渲染有顺序
                   fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
                   #将获取到的fields赋值给_unbound_fields
                   cls._unbound_fields = fields
            if cls._wtforms_meta is None:
                bases = []
                for mro_class in cls.__mro__:
                  if 'Meta' in mro_class.__dict__:
                      #mro_class是<class 'wtforms.form.Form'>
                      #mro_class.Meta是<class 'wtforms.meta.DefaultMeta'>
                      bases.append(mro_class.Meta)
                  #这里创建了Meta类并赋值给cls._wtforms_meta
                cls._wtforms_meta = type('Meta', tuple(bases), {})
            #这里调用__call__时调用了Form的__init__方法
            return type.__call__(cls, *args, **kwargs)
    这里调用__call__时调用了Form的__init__方法(为什么?你居然问为什么!!!):
    class Form(with_metaclass(FormMeta, BaseForm)):
        def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
            meta_obj = self._wtforms_meta()
            #这里又调用了父类的初始化方法,我们去看下都做了什么?
            super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
    class BaseForm(object):
        def __init__(self, fields, prefix='', meta=DefaultMeta()):
            self._fields = OrderedDict()
            for name, unbound_field in itertools.chain(fields, extra_fields):
                #循环遍历fields,self._fields中有所有的字段
                options = dict(name=name, prefix=prefix, translations=translations)
                field = meta.bind_field(self, unbound_field, options)
                ##OrderedDict([('name', <wtforms.fields.core.StringField object at 0x0000006A6240A438>)])
                self._fields[name] = field
    当上述代码赋值完毕后,就会执行这些个代码:
    class Form(with_metaclass(FormMeta, BaseForm)):
        def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
            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)
            #上述代码已经跑完,现在开始从这里执行代码,这里的self._fields有自定义的Form中的所有的字段
            for name, field in iteritems(self._fields):
                setattr(self, name, field)
            #此时执行self.process方法,自己这里没process方法,然后就去基类中执行,这时程序跳到了BaseForm中的process方法
            self.process(formdata, obj, data=data, **kwargs)
    基类BaseForm中process方法:
    class BaseForm(object):
        def process(self, formdata=None, obj=None, data=None, **kwargs):
            formdata = self.meta.wrap_formdata(self, formdata)
            if data is not None:
                kwargs = dict(data, **kwargs)
            #因为formdata和data都为None所以程序执行到了这里:
            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:
                    #最后执行到这里,这是调用process是调用了字段对象StringField的process方法,自己没有这个方法跑到了Field中去:
                    field.process(formdata)
    Field中的代码:
    class Field(object):
        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])
    
        #由于formdata,self.filters字段为空,下段代码不执行。
          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])
        至此所有的流程都已经跑完,页面已经显示控件内容,但是有两个疑问:
        1. 没有看到页面处理代码,就是生成html的代码?
        2. 那个UnboundField是怎样在代码执行期间起作用的?
        3. 数据是如何校验的?
    
        首先解决第一个问题,再加载LoginForm时:
    class LoginForm(Form):
        name = StringField(
            label='姓名',
            validators=[
                DataRequired('不能为空!')
            ],
            render_kw={
                'placeholder': '请输入姓名!'
            }
    
        )
    name是StringField类的对象,那么加载StringField时内部都做了什么?
    class StringField(Field):
        #这里实例化一个控件,接着我们看下TextInput()里面做了什么?
        widget = widgets.TextInput()
    
    class TextInput(Input):
        #这个类加括号调用了基类的__call__方法,我们看下基类:
        input_type = 'text'
    
    class Input(object):
        #我们看到这里渲染了html标签并返回
        def __call__(self, field, **kwargs):
           kwargs.setdefault('id', field.id)
           kwargs.setdefault('type', self.input_type)
           if 'value' not in kwargs:
               #这里会调用_value()给相关标签添加一个'value='的html属性
               kwargs['value'] = field._value()
            return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
    #我们看下基类
    class Field(object):
        def __str__(self):
           #返回了一个代表html的对象,加括号,又调用了__call__()
           return self()
        def __call__(self, **kwargs):
            #关键代码,我们接着看下render_field
            return self.meta.render_field(self, kwargs)
    #这里来到了meta.py
    class DefaultMeta(object):
        def render_field(self, field, render_kw):
           #这里控制render的实现,默认调用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)
           #接着调用StringField的静态字段对控件进行最终的渲染
           return field.widget(field, **render_kw)
    那个UnboundField是怎样在代码执行期间起作用的:
    首先在实例化LoginForm内的字段StringField时会调用父类的__new__方法:
    class Field(object)
        def __new__(cls, *args, **kwargs):
            if '_form' in kwargs and '_name' in kwargs:
                return super(Field, cls).__new__(cls)
           else:
               #返回一个UnboundField对象,对StringField的对象内容进行了封装,实例化UnboundField调用\__init__
                return UnboundField(cls, *args, **kwargs)
    
    #看下UnboundField的执行流程:
    class UnboundField(object):
        _formfield = True
        #首先这里的计数,是根据字段的书写顺序进行的加减,为了排序使用
        creation_counter = 0
        #现在的self:<UnboundField(StringField, (), {'label': '姓名', 'validators': [<wtforms.validators.DataRequired object at 0x0000009A20B948D0>], 'render_kw': {'placeholder': '请输入姓名!'}})>
        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
    现在页面的初始化工作还没有开始,只是将页面初始化所需要的组件全部加载到内存,内存的初始化工作已经完毕!
    接着我们发送个get请求,实例化LoginForm:
    @home.route('/')
    def index():
        form = LoginForm()
        return render_template('login.html', form=form)
    调用了父类的call方法:
    class FormMeta(type):
        def __call__(cls, *args, **kwargs):
            if cls._unbound_fields is None:
                fields = []
                for name in dir(cls):
                    if not name.startswith('_'):
                        unbound_field = getattr(cls, name)
                        if hasattr(unbound_field, '_formfield'):
                            #fields里有username对应的UnboundField
                            fields.append((name, unbound_field))
                #对field进行了排序,字段的先后顺序决定了页面的显示顺序
                fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
                #现在_unbound_fields 中有所有的字段
                cls._unbound_fields = fields
           if cls._wtforms_meta is None:
               bases = []
               for mro_class in cls.__mro__:
                   if 'Meta' in mro_class.__dict__:
                      bases.append(mro_class.Meta)
               #创建了一个Meta类继承<class 'list'>: [<class 'wtforms.meta.DefaultMeta'>]
               cls._wtforms_meta = type('Meta', tuple(bases), {})
    
           return type.__call__(cls, *args, **kwargs)
    最后return type.__call__(cls, *args, **kwargs)时来到了class Form(with_metaclass(FormMeta, BaseForm))的init方法:
    class Form(with_metaclass(FormMeta, BaseForm)):
        def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
            #得到一个DefaultMeta的对象
            meta_obj = self._wtforms_meta()
            #这里又执行了父类BaseForm的__init__()方法
            super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
        #这句话的意思是给每个字段对象设置一个属性,可以obj.attr获取值
        for name, field in iteritems(self._fields):
            setattr(self, name, field)
        #这里去处理数据,校验数据
        self.process(formdata, obj, data=data, **kwargs)
    BaseForm的__init__()方法
    class BaseForm(object):
        def __init__(self, fields, prefix='', meta=DefaultMeta()):
            self._fields = OrderedDict()
        #遍历循环fields
        for name, unbound_field in itertools.chain(fields, extra_fields):
           options = dict(name=name, prefix=prefix, translations=translations)
           #这里将所有Fields实例化完毕
           field = meta.bind_field(self, unbound_field, options)
           #将值赋给_fields
           self._fields[name] = field
    看下这个:field = meta.bind_field(self, unbound_field, options)
    class DefaultMeta(object):
        def bind_field(self, form, unbound_field, options):
            #这里又跑到了UnboundField类中执行bind方法
            return unbound_field.bind(form=form, **options)
    class UnboundField(object):
        #看下关键参数,别的就不需要看了:
        #form=<app.home.forms.LoginForm object at 0x000000DE0AEA9EF0>
        #name='username'
        def bind(self, form, name, prefix='', translations=None, **kwargs):
            kw = dict(
                self.kwargs,
                _form=form,
                _prefix=prefix,
                _name=name,
                _translations=translations,
                **kwargs
            )
            #这里去创建StringFiled类并实例化
            return self.field_class(*self.args, **kw)
    创建StringFiled类并实例化:
    class Field(object):
        def __new__(cls, *args, **kwargs):
            if '_form' in kwargs and '_name' in kwargs:
                #现在kwargs中有值,创建一个类:
                return super(Field, cls).__new__(cls)
            else:
                #这个是刚开始加载到内存时,刚开始创建Field类时会返回UnboundField:
                return UnboundField(cls, *args, **kwargs)
        #创建完后去初始化这个类得到StringFiled对象:
        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):
    我们先看下当点击:
    Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 
    这时的代码流程,这时是去创建Form并实例化: 
    class Form(with_metaclass(FormMeta, BaseForm)):
         def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
             #主要去处理数据,调用了基类的self.process方法,看下面:
             self.process(formdata, obj, data=data, **kwargs)
    
    class BaseForm(object):
        def process(self, formdata=None, obj=None, data=None, **kwargs):
            #这时调用了字段自己的process方法,
             field.process(formdata)
    
    class Field(object):
        def process(self, formdata, data=unset_value):
            #这里的self指Field子类对象
            self.process_data(data)
    #这里处理了数据
    def process_data(self, value):
        self.data = value
  • 相关阅读:
    Linux防火墙开放某端口号
    MySQL的权限管理
    Linux安装Node.js
    Eclipse上传新项目到GitLab
    Linux安装Nexus
    Linux安装中文字体_宋体
    Linux用户管理
    Linux安装MySQL_5.6
    reduce基本用法,js实现分组
    js 数字格式化
  • 原文地址:https://www.cnblogs.com/llhtjwq/p/8664095.html
Copyright © 2011-2022 走看看