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实例化而来,而实例化一个类,先执行该类的__new__方法来创建这个类,然后调用__init__()方法来实例化

,本类中没有就去父类中找,

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=1name = 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=2pwd = simple.PasswordField(label='密码',validators=[validators.DataRequired(message='密码不能为空.'),validators.Length(min=8, message='用户名长度必须大于%(min)d'),validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{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中

如果一个类有metaclass,那么该类创建的时候会执行她的metaclass类中的__init__()方法,实例话这个类回执行 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 inheritancehierarchy."""if cls._unbound_fields is None:fields = []#当前类所有的属性for name in dir(cls):if not name.startswith('_'):#得到UnboundField()对象unbound_field = getattr(cls, name)                    #UnboundField()对象默认_formfield为Trueif 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` orequivalent. formdata should be some sort of request-data wrapper whichcan get multiple parameters from the form input, and values are unicodestrings, e.g. a Werkzeug/Django/WebOb MultiDict:param obj:If `formdata` is empty or not provided, this object is checked forattributes matching form field names, which will be used for fieldvalues.:param prefix:If provided, all fields will have their name prefixed with thevalue.: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 attributeson this form's meta instance.:param `**kwargs`:If `formdata` is empty or not provided and `obj` does not containan attribute named the same as a field, form will assign the valueof 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)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)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 thevalue.:param meta:A meta instance which is used for configuration and customizationof WTForms behaviors."""if prefix and prefix[-1] not in '-_;:/.':prefix += '-'self.meta = metaself._prefix = prefixself._errors = Noneself._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

b.执行UnboundField中的bind()方法:

class UnboundField(object):_formfield = Truecreation_counter = 0def __init__(self, field_class, *args, **kwargs):UnboundField.creation_counter += 1self.field_class = field_classself.args = argsself.kwargs = kwargsself.creation_counter = UnboundField.creation_counterdef 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'sdefault.Field subclasses usually won't override this, instead overriding theprocess_formdata and process_data methods. Only override this forspecial advanced processing, such as when a field encapsulates manyinputs."""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])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])

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

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

   def __str__(self):"""Returns a HTML representation of the field. For more powerful rendering,see the `__call__` method."""return self()

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

 def __call__(self, **kwargs):"""Render this field as HTML, using keyword args as additional attributes.This delegates rendering to:meth:`meta.render_field <wtforms.meta.DefaultMeta.render_field>`whose default behavior is to call the field's widget, passing anykeyword arguments from this call along to the widget.In all of the WTForms HTML widgets, keyword arguments are turned toHTML attributes, though in theory a widget is free to do anything itwants with the supplied keyword arguments, and widgets don't have toeven do anything related to HTML."""return self.meta.render_field(self, kwargs)

最终返回值是meta.render_field(self, kwargs)执行后的结果

    def render_field(self, field, render_kw):"""render_field allows customization of how widget rendering is done.The default implementation calls ``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)return field.widget(field, **render_kw)

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

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 = Nonesuccess = Truefor 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 = Falsereturn successc. 每个字段进行验证时候字段的pre_validate 【预留的扩展】字段的_run_validation_chain,对正则和字段的钩子函数进行校验字段的post_validate【预留的扩展】

转载于:https://www.cnblogs.com/ctztake/p/8260487.html

wtforms Form实例化流程(源码解析)相关推荐

  1. Spring源码解析(五)-Bean的实例化流程(上)

    在前面已经完成了对需要实例化bean的收集并封装成BeanDefinition,并且将BeanPostProcess等组件进行了提前实例化.接下来就到了容器启动的最后一步,也是最复杂的一步-实例化be ...

  2. Netty 源码解析系列-服务端启动流程解析

    netty源码解析系列 Netty 源码解析系列-服务端启动流程解析 Netty 源码解析系列-客户端连接接入及读I/O解析 五分钟就能看懂pipeline模型 -Netty 源码解析 1.服务端启动 ...

  3. Flink源码解析 | 从Example出发:理解Flink启动流程

    从<Apache Flink本地部署>这篇文章中可以看到,我们启动集群都是通过脚本start-cluster.sh开始执行. 我们的源码解析之路就从flink的bash脚本入手. star ...

  4. 从源码解析-结合Activity加载流程深入理解ActivityThrad的工作逻辑

    ActivityThread源码解析 前言 类简称 类简介 一 二 三 四 五 代理和桩的理解 ActivityThread ActivityThread.main AT.attach AMN.get ...

  5. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  6. 实例源码_SpringBoot数据库源码解析Template实例化操作

    Jdbc TemplateAutoConfiguration 在实践过程中,除了数据源的配置外,我们还会经常用到 Jdbc Template.Jdbc Template是 Spring 对数据库的操作 ...

  7. Retrofit2源码解析——网络调用流程(下)

    Retrofit2源码解析系列 Retrofit2源码解析(一) Retrofit2源码解析--网络调用流程(上) 本文基于Retrofit2的2.4.0版本 implementation 'com. ...

  8. .net core 源码解析-mvc route的注册,激活,调用流程(三)

    .net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...

  9. statement执行insert into语句_【图文并茂】源码解析MyBatis ShardingJdbc SQL语句执行流程详解...

    源码分析Mybatis系列目录: 1.源码分析Mybatis MapperProxy初始化[图文并茂] 2.源码分析Mybatis MappedStatement的创建流程 3.[图文并茂]Mybat ...

最新文章

  1. verilog实现步进电机脉冲分配器(三相六拍)基于Nexys4DDR开发板
  2. concat函数显示小数点包括0
  3. MongoDB-概述:跨平台的面向文档的高性能高可用性易扩展数据库
  4. 商业计划书模板(高质量)
  5. c3p0数据库连接池配置
  6. Unity 动态更改鼠标样式
  7. LayUI表单验证select定位失效问题
  8. 【计算机基础】防火墙
  9. php fakepath,php 46 模板替换 上传附件fakepath json编码入库转义符
  10. matlab中二阶偏导数,matlab中二元函数的一阶和二阶偏导数
  11. 服务器安装macos虚拟机,Win10虚拟机VMware安装黑苹果MacOS Sierra图文教程
  12. CTF---青青子衿(无C++注册机)
  13. 【Redis】Redis常用命令
  14. python 天气预报制作_Python 制作语音天气预报播报程序
  15. Linux安全之三大攻击(SYN,DDOS,CC)原理及处理的详解
  16. 华为操作系统鸿蒙 hms生态系统,华为HMS生态系统服务是什么 鸿蒙操作系统机会来...
  17. [益智]:为什么下水道的盖子是圆的?
  18. RPG游戏角色生成器
  19. 简单一步,轻松下载B站视频 | 实用技能get
  20. PID控制中Kp Ki Kd 在控制质量中的影响

热门文章

  1. 【计算机网络笔记】计算机网络定义分类
  2. 【二分法】剑指offer:二维数组中的查找
  3. 视频质量,分辨率,码率之间的关系 2
  4. vb编写各种趣味小程序_【VB小程序】来测测你的打字速度吧
  5. height、clientHeight、scrollHeight、offsetHeight区别
  6. 【NOIP2010】【Luogu1190】接水问题(给定顺序的模拟)
  7. java上拉变量_「小程序JAVA实战」小程序页面的上拉下拉刷新(50)
  8. python脚本用类编写_跟老齐学Python之编写类之二方法
  9. 第五章 Windows基础控件
  10. 碳钢腐蚀速率计算公式_化工管道用金属材料的腐蚀