flask源码系列之-wtforms

一:运行前form内部程序流程

1,开始form

class Loginform(Form):
    name=simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名不能为空'),
            validators.Length(min=5,max=11,message='长度不能小于%(min)d,不能大于%(max)d')
        ],
        widget=widgets.TextInput(),
        render_kw={'class':'form-control'}
    )

2,程序开始运行时

form=LoginForm()
    这边LoginForm()运行的时候,而loginForm是继承Form,会先执行元类也就是type的__call__方法,然后执行自己的__new__方法,__init__
方法.
3,Form进去,是继承了一个函数
class Form(with_metaclass(FormMeta, BaseForm)):

    a,  这个函数是用Form继承一个元类FormMeta,
        def with_metaclass(meta, base=object):
        return meta("NewBase", (base,), {})

        这个函数就是形成  FormMeta('NewBase',(BaseForm,),{}) ==>NewBase(BaseForm),用一个元类创建一个新类.
        并且在这里FormMeta('NewBase',(BaseForm,),{}),也就是调用FormMeta()的__init__方法,如果有__new__的话,
        先执行__new__方法.  PS:这里formmate相当于type

4, LoginForm()实例化,就会自动执行元类的__call__方法.他的元类是FormMeta.

def __call__(cls, *args, **kwargs):
    if cls._unbound_fields is None:
        fields = [] #['name':name_obj]
        for name in dir(cls):
            #dir(cls)打印出来里面所有的参数,包括我们穿进去的参数.
            if not name.startswith('_'):
                unbound_field = getattr(cls, name) #拿到对象
                if hasattr(unbound_field, '_formfield'):

                    fields.append((name, unbound_field))

        fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
        cls._unbound_fields = fields
        ----------------这里做了两件事
        ---找到类的静态字段,也就是我LoginForm下面的name,pwd的字段,循环保存到cls._unbound_fields={name:unbound_field}.
        ---另外让他们按照进来的顺序排序

    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类.
        cls._wtforms_meta = type('Meta', tuple(bases), {})
        #cls._wtforms_meta=所有创建的Meta类
    return type.__call__(cls, *args, **kwargs)
    ---------这里循环该类的所有父类,如果找到Meta类,name就创建Meta
View Code

5,执行完元类的__call__方法,现在要执行自己的__new__方法和__init__方法,显然自己没有__new__方法,所有执行__init__方法.

def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
    meta_obj = self._wtforms_meta()     #self._wtforms_meta()是这个Meta类的实例化
        #meta可以传进去一个字典{},并当做默认值再html页面显示出来
        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)
        --这里super执行父类的__init__方法,就是BaseForm---

        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.name=form.field
        self.process(formdata, obj, data=data, **kwargs)
View Code

6,执行super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix),

调用父类BaseForm的__init__方法.
class BaseForm(object):
    def __init__(self, fields, prefix='', meta=DefaultMeta()):
        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): #这里循环取到(name,unbound_field)
            options = dict(name=name, prefix=prefix, translations=translations)

            #并且实例化对象
            field = meta.bind_field(self, unbound_field, options)
            self._fields[name] = field
            #这里拿到原来字典里的unbound_field替换成Form类的字段String对象.
            #现在的self._fields={name:string}
View Code

7,再回到Form类的__init__里面,执行

for name, field in iteritems(self._fields):
    setattr(self, name, field)      #给name赋值上string属性
self.process(formdata, obj, data=data, **kwargs)

8,执行self.process(formdata, obj, data=data, **kwargs)

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)
            #kwargs={data:{}}也就是穿进去参数的第二种形式data传参,但是必须是字典形式
        for name, field, in iteritems(self._fields):
            if obj is not None and hasattr(obj, name):  #通过obj形式接收参数
                field.process(formdata, getattr(obj, name))
            elif name in kwargs:
                field.process(formdata, kwargs[name])
            else:
                field.process(formdata)  #
     这里主要设置几种值的赋值方法,
     formdata接收getlist格式的参数   像request.form
     data:接收字典格式键值对         {name:'12'}
     obj:接收对象
View Code

二:字段继承Field

1,name=simple.StringField()

StringField继承Field
当StringField实例化时,首先执行父类的__new__方法,再执行自己__init__方法.
class Field(object):
   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)
View Code

2,这里执行__new__方法时,数值为空时,执行UnboundField(cls, *args, **kwargs).

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
PS:这里开始设置了2个静态字段,creation_counter = 0,在__init__里
UnboundField.creation_counter += 1
所以当实例化一个对象时,则creation_counter+=1.
View Code

3,执行__init__方法,因为自己没有,所以用父类的. 初始化数据

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):
    if widget is not None:
    self.widget = widget

    for v in itertools.chain(self.validators, [self.widget]):
        flags = getattr(v, 'field_flags', ())
        for f in flags:
            setattr(self.flags, f, True)
View Code

三:开始校验

form=Loginform(formdata=request.form)
if form.validate():

return '登录成功'
return render_template('login.html',title='Sign In',form = form)

1,首先还是一样LoginForm()开始进行实例化,然后走里面的代码(略过).
if form.validate() 开始走validate,进行校验.
    extra = {}  #{name:string_obj,pwd:}
        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)

    #这里就是判断是否有钩子函数,并且运行父类validate方法.

2,

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

3,如果if not field.validate(self, extra):就开始运行field.validate,

def validate(self, form, extra_validators=tuple()):
    self.errors = list(self.process_errors)
    stop_validation = False
            try:
        self.pre_validate(form)
    except StopValidation as e:
        if e.args and e.args[0]:
            self.errors.append(e.args[0])
        stop_validation = True
    except ValueError as e:
        self.errors.append(e.args[0])

    # Run validators
    if not stop_validation:     #如果没有报错则运行这里
        chain = itertools.chain(self.validators, extra_validators)  #把所有的错误信息合在一起
        stop_validation = self._run_validation_chain(form, chain)

    # Call post_validate
    try:
        self.post_validate(form, stop_validation)
    except ValueError as e:
        self.errors.append(e.args[0])
View Code

4,

stop_validation = self._run_validation_chain(form, chain)
        for validator in validators:
            try:
                validator(form, self)
            except StopValidation as e:
                if e.args and e.args[0]:
                    self.errors.append(e.args[0])
                return True
            except ValueError as e:
                self.errors.append(e.args[0])

        return False
把所有的信息的错误信息加到errors里面,然后返回内容
最后if form.validate():   接收这里的返回信息.
如果是True则运行里面的内容,如果是FALSE,则不运行内容.

四,渲染页面

未做

 



原文地址:https://www.cnblogs.com/52forjie/p/8295153.html