首先创建wtforms

from wtforms.form import Form
# 引入Form元素父类
from wtforms import StringField
# 引入Form验证父类
from wtforms.validators import DataRequiredclass 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):#   passreturn 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为NewBasetype.__init__(cls, name, bases, attrs)#给NewBase设置属性:初始值为空cls._unbound_fields = Nonecls._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_fieldscls._unbound_fields = fieldsif 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_metacls._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.defaultself.object_data = datatry:#调用该方法为了处理适用于该字段对象的值并保存结果。
            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_fieldreturn 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 += 1self.field_class = field_classself.args = argsself.kwargs = kwargsself.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 = fieldsif 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()#遍历循环fieldsfor 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)#将值赋给_fieldsself._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

转载于:https://www.cnblogs.com/qq2233297039/articles/8665481.html

flask—wtforms相关推荐

  1. Flask wtforms

    wtforms 简介 WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 安装: ? 1 pip3 install wtforms 用户登录注册示例 1. 用户登录 ...

  2. flask WTForms

    WTForms由三个主要部分组成:字段,检验器和表单 字段代表输入域,做初步的检查 检验器是附加在字段上的函数,确保表单中的数据符合要求 表单是python类,包含了字段和检验器 表单 描述 Date ...

  3. Flask开发微电影网站(一)

    1.用到的Flask知识 1.使用整形,浮点型,路径型,字符串型下正则表达式路由转化器 2.使用GET与POST请求,上传文件,cookie获取与响应,404处理 3.使用模板自动转义,定义过滤器,定 ...

  4. Flask项目之个性化微电影网站的实战开发(一)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 项目源码下载 目录 一丶叙述 二丶Windows环境搭建 三丶项目分析丶搭建目录以及模型设计 四丶搭建前台页面 一丶叙述 1.项目 ...

  5. python视频网站项目_[项目实战] Python Flask构建微电影视频网站

    注:本站源码仅做学术研究,自娱自乐使用,不得用于任何的非法商业用途 由于版权原因.本站将无限期停止微擎模块资源下载如果有任何侵犯您权益的内容请联系小站删除哦.小站一定会积极配合的. 课程目录: (下载 ...

  6. python视频网站项目_Python超羽量级灵活框架 Flask实战微电影视频网站 大小项目通吃 Flask实战项目...

    课程名称: Python超羽量级灵活框架 Flask实战微电影视频网站 大小项目通吃 Flask实战项目视频教程 课程简介: Flask作为Python编写的Web小型框架,可以使用Python快速开 ...

  7. Python-Flask开发微电影网站(一)

    1.用到的Flask知识 1.使用整形,浮点型,路径型,字符串型下正则表达式路由转化器 2.使用GET与POST请求,上传文件,cookie获取与响应,404处理 3.使用模板自动转义,定义过滤器,定 ...

  8. python 开发视频播放网站_python开发微电影视频网站教程附源码

    做为一个合格的Pythoner,只掌握一个框架是不够,如果你有过其它Python框架(例如django)的使用经验,对比不同框架的特性来学习,你将收获更多 当前信息若含有黄赌毒等违法违规不良内容,请点 ...

  9. android反编译后有中文乱码,android反编译查看源码,apk解压后XML乱码解决

    详解MySQL的用户密码过期功能 这篇文章主要为大家详细介绍了MySQL的用户密码过期功能的相关资料,需要的朋友可以参考下   Payment Card Industry,即支付卡行业,PCI行业表示 ...

  10. (旧)3- Flask构建弹幕微电影网站- 课程介绍

    Flask 构建微电影视频网站 已上线演示地址: http://movie.mtianyan.cn 项目源码地址:https://github.com/mtianyan/movie_project 持 ...

最新文章

  1. jmeter的xpath断言 [XPath Assertion]
  2. Spring中 @Autowired注解与@Resource注解的区别
  3. 2.10 词嵌入除偏-深度学习第五课《序列模型》-Stanford吴恩达教授
  4. Java黑皮书课后题第5章:**5.38(十进制转八进制)编写程序,提示用户输入一个十进制整数,然后显示对应的八进制值。(不要是使用Java的Integer类的任何方法)
  5. Spark Structure Streaming(一)之简介
  6. ElasticSearch 索引、更新和删除数据
  7. spring 改变url
  8. 应用高斯分布来解决异常检测问题(一)
  9. python按行读取csv文件_python.csv 按行按列读取
  10. uni 登录token方法_uniapp如何检验用户是否登录
  11. 暗黑模式开发一些心得
  12. safari查看html代码,如何在Mac上的Safari中查看网页的源代码 | MOS86
  13. C++中i++和++i的区别
  14. clover更新驱动 后不能开机_Big Sur安装常见问题【持续更新】
  15. 人工智能学习:CS188
  16. SpringBoot系统监控,oshi版
  17. 【论文】Track to Detect and Segment: An Online Multi-Object Tracker
  18. 青海省国家湿地公园功能区划数数据、全国湿地沼泽分布数据、全国省市县自然保护区
  19. 图文讲解如何使用QQ企业邮箱绑定域名开通企业邮箱(使用时代互联的域名管理后台)
  20. 单样本和双样本的检验

热门文章

  1. 已知坐标求方位角_震惊,up主居然能默写球坐标拉普拉斯算子!
  2. 对变量移位顺序读写_Java多线程并发读写锁ReadWriteLock实现原理剖析
  3. 【数据库原理实验(openGauss)】事务与并发控制
  4. clickhouse 数据存储
  5. hive判断是否包含
  6. Linux嵌入式开发_修改镜像文件输出路径
  7. Please make sure you have the correct access rights and the repository exists.
  8. [转载]基于TFS实践敏捷-工作项跟踪
  9. tornado的views(templates)
  10. linux awk数组使用