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-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中
如果一个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【预留的扩展】
本文来自博客园,作者:秋华,转载请注明原文链接:https://www.cnblogs.com/qiu-hua/p/12636407.html