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)
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)
调用父类的初始化方法,那么我们需要看一下BaseForm中都做了什么:
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):
执行项目
这时的代码流程,这时是去创建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