• python-flask-wtforms组件流程源码


      在最开始要弄明白一点,类都是由元类创建的。在定义类 class Foo:pass的时候(类也是对象),就会执行type类或者type派生类的__init__方法,当Foo()时:执行type类或者type派生类的__call__方法,在__call__方法中调用了Foo类的__new__方法创建了一个对象,接着执行__init__方法为这个创建的对象进行赋值属性。

    from flask import Flask, render_template, request, redirect
    from wtforms import Form
    from wtforms.fields import core
    from wtforms.fields import html5
    from wtforms.fields import simple
    from wtforms import widgets
    from wtforms import validators
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    #0 定义LogonForm类
    class LoginForm(Form):
       #1 StringField类的实例化 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 = simple.PasswordField( label='密码', validators=[ validators.DataRequired(message='密码不能为空.'), validators.Length(min=8, message='用户名长度必须大于%(min)d'), validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[$@$!%*?&])[A-Za-zd$@$!%*?&]{8,}", message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符') ], widget=widgets.PasswordInput(), render_kw={'class': 'form-control'} ) class Meta: csrf = False def validate_pwd(self,*args,**kwargs): pass @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET':
         #2.实例化一个LoginForm对象 form
    = LoginForm()
         #第3步
         # print(form.name)
    return render_template('login.html', form=form) else:
         #2.3步 form
    = LoginForm(formdata=request.form)
         #4步,验证
    if form.validate(): print('用户提交数据通过格式验证,提交的值为:', form.data) else: print(form.errors) return render_template('login.html', form=form) def test(): form = LoginForm() if __name__ == '__main__': app.run()

    第0步:

    在定义LoginForm类的时候我们看看发生了什么

    首先我们要知道metaclass的另外一种方式:with_metaclass

    metaclass的另外一种方式:
        class MyType(type):
            def __init__(self,*args,**kwargs):
                print('xxxx')
                super(MyType,self).__init__(*args,**kwargs)
    
            def __call__(cls, *args, **kwargs):
                obj = cls.__new__(cls,*args, **kwargs)
                cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
                return obj
    
        def with_metaclass(base):
            return MyType("MyType",(base,),{})
    
        class Foo(with_metaclass(object)):
            def __init__(self,name):
                self.name = name
    
        #打印结果:  xxxx    xxxx

    所以我们去Form中找找,发现了metaclass的另外一种方式

    class Form(with_metaclass(FormMeta, BaseForm)):
        pass

    我们再去with_metaclass看看

    def with_metaclass(meta, base=object):
        return meta("NewBase", (base,), {})
       # FormMeta("NewBase", (BaseForm,), {}) # 通过FormMeta创建了一个NewBase类,NewBase类继承了BaseForm类
       # 那你有没有疑问,为什么 FormMeta类可以创建类呢? 我们去FormMeta类看看
    class FormMeta(type):
      pass
    #发现FormMeta继承了type类,所以刚才我们的疑问迎刃而解。

    那就是说当LoginForm定义的时候执行了FormMeta类的__init__方法

    class FormMeta(type):
        def __init__(cls, name, bases, attrs):
            type.__init__(cls, name, bases, attrs)
            cls._unbound_fields = None
            cls._wtforms_meta = None

    这步完成后 LoginForm类有两个属性:cls._unbound_fields = None和  cls._wtforms_meta = None

    第1步:实例化StringField类的对象,首先应该去StringField中找__new__方法

    class StringField(Field):
        pass
    #发现StringField类中没有,那我们就去基类中
    class Field(object):
            def __new__(cls, *args, **kwargs):
           #判断不成立,走else   
    if '_form' in kwargs and '_name' in kwargs:   return super(Field, cls).__new__(cls)   else:
             #返回一个UnboundField对象   
    return UnboundField(cls, *args, **kwargs) # cls = simple.StringField 这个类
    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.field_class = simple.StringField
            self.args = args
            self.kwargs = kwargs
            self.creation_counter = UnboundField.creation_counter    #这个数字,在后面会根据这个进行排序

    这步完成后,我们知道 LoginForm的 name和pwd字段都等于UnboundField 的对象

    第2步:实例化LoginForm的对象会执行FormMeta的__call__方法

    class FormMeta(type):
        def __call__(cls, *args, **kwargs):
            if cls._unbound_fields is None:
                fields = []
                #获取LoginForm类中的所有字段
                for name in dir(cls):
                    if not name.startswith('_'):
                        #获取该字段的值
                        unbound_field = getattr(cls, name)  #unbound_field 是一个UnboundField对象
                        if hasattr(unbound_field, '_formfield'):         #    _formfield = True
                            fields.append((name, unbound_field))         # [("name",UnboundField对象),("pwd",UnboundField对象)]
                fields.sort(key=lambda x: (x[1].creation_counter, x[0]))  #根据UnboundField对象的creation_counter属性对fields列表进行排序
                cls._unbound_fields = fields                             # LoginForm._unbound_fields = [("name",UnboundField对象),("pwd",UnboundField对象)]
    
            if cls._wtforms_meta is None:
                bases = []
                for mro_class in cls.__mro__:   #循环当前类和基类组成的元组
                    if 'Meta' in mro_class.__dict__:   #如果类中有Meta类,就把Meta类添加到 bases列表中
                        bases.append(mro_class.Meta)
                cls._wtforms_meta = type('Meta', tuple(bases), {})    #LoginForm._wtforms_meta = 一个新的Meta类,它继承了所有的Meta类,这样做好处在于:通过新Meta类可以取到无论是LoginForm或者LoginForm基类中的任何Meta类
            return type.__call__(cls, *args, **kwargs)

    接着到LoginForm或基类中找__new__方法,发现都没有,那就继续找LoginForm或基类中的__init__方法

    class Form(with_metaclass(FormMeta, BaseForm)):
        Meta = DefaultMeta
        
        def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
            meta_obj = self._wtforms_meta()
         #2.1 执行父类的__init__方法
    super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)      #2.2 for name, field in iteritems(self._fields):
           #这个self是LoginForm对象 setattr(self, name, field)
         #2.3步 self.process(formdata, obj, data
    =data, **kwargs)

    这时候要注意:Form的基类是with_metaclass函数创建的 NewBase类,NewBase类继承了BaseForm

    所以第2.1步:执行了BaseForm类的__init__方法

    class BaseForm(object):
        #这个self是LoginForm对象
        def __init__(self, fields, prefix='', meta=DefaultMeta()):
            self.meta = meta              #meta是新创建的Meta类的对象
            self._fields = OrderedDict()
    
            extra_fields = []
            #从这句话我们可以看出 在自定义LoginForm中的Meta类内可以写字段: csrf = False
            if meta.csrf:
                self._csrf = meta.build_csrf(self)
                extra_fields.extend(self._csrf.setup_form(self))
            #第2.1.1步
            for name, unbound_field in itertools.chain(fields, extra_fields):
           #name 是LoginForm中的字段,unbound_field是 UnboundField对象 field
    = meta.bind_field(self, unbound_field, options)
           #第2.1.2步: 到有序字典中赋值 self._fields[name]
    = field # {"name":simple.StringField类对象,"pwd":simple.PasswordField类对象}

    第2.1.1步:执行meta.bind_field方法

    class DefaultMeta(object):
        def bind_field(self, form, unbound_field, options):
         #2.1.1.1 form是LoginFrom对象
    return unbound_field.bind(form=form, **options)

    在 这里你没有疑问:meta是新创建的Meta类对象,为什么会执行 DefaultMeta类的bind_field方法呢?

    那是因为新Meta类继承了 LoginForm类和其基类中的所有Meta类

    class Form(with_metaclass(FormMeta, BaseForm)):
        Meta = DefaultMeta

    看到上面这段代码之后你就明白为什么会跑到DefaultMeta类中找方法了吧

    第2.1.1.1步:

    class UnboundField(object):
        def bind(self, form, name, prefix='', translations=None, **kwargs):
            #就是在这步把_form当参数传进field_class方法
            kw = dict(
                self.kwargs,
                _form=form,
                _prefix=prefix,
                _name=name,
                _translations=translations,
                **kwargs
            )
         #2.1.1.1.1步
            return self.field_class(*self.args, **kw)
           #在第1步中我们可以看到 self.field_class = simple.StringField

    把各自字段相应的类实例化的对象返回给2.1.1步,然后又赋值给了 field 变量

    第2.1.1.1.1步:执行simple.StringField或其基类的__init__方法

    class Field(object):
        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 _meta is not None:
                self.meta = _meta
         #_form=form
    elif _form is not None: self.meta = _form.meta #self.meta = form.meta = 新创建的Meta类对象 else: raise TypeError("Must provide one of _form or _meta") self.render_kw = render_kw self.name = _prefix + _name self.type = type(self).__name__ self.id = id or self.name

    故,2.1步执行完成后     form(LoginForm对象)._fields["name"] = simple.StringField类对象

    第2.2步:赋值操作

    form.name=simple.StringField类对象
    form.pwd=simple.PasswordField类对象

    第2.3步:此步是实例化LoginForm时传值的情况分析

     根据此行代码我们可以看出,数据初始化传值有三种形式: 1.  data=字典, 2.  obj=对象.字段 , 3. formdata=有getlist方法
    self.process(formdata, obj, data=data, **kwargs)
    例如:
     class User:
    def __init__(self,name):
    self.name = name
    obj = User('alex')

    form = LoginForm(data={'name':'alex'})
    form = LoginForm(obj=obj)
    form = LoginForm(formdata=request.form/args)
    class BaseForm(object):
        def process(self, formdata=None, obj=None, data=None, **kwargs):
         #第2.3.1步
            formdata = self.meta.wrap_formdata(self, formdata)
            if data is not None:
                kwargs = dict(data, **kwargs)
    
            for name, field, in iteritems(self._fields):
           #name = name ,field = simple.StringField类对象
           #传值第2种方式
    if obj is not None and hasattr(obj, name): field.process(formdata, getattr(obj, name))
           #传值第1种方式
    elif name in kwargs: field.process(formdata, kwargs[name])
           #传值第3种方式
    else:
              # 第 2.3.2步 field.process(formdata)

    第2.3.1步

    class DefaultMeta(object):
    def wrap_formdata(self, form, formdata):
         #判断传进来的formdata有没有getlist方法
    if formdata is not None and not hasattr(formdata, 'getlist'): if hasattr(formdata, 'getall'): return WebobInputWrapper(formdata) else: raise TypeError("formdata should be a multidict-type wrapper that supports the 'getlist' method") return formdata

     第2.3.2步:

    class Field(object):
       #self 是 field对象
    def process(self, formdata, data=unset_value):      #formdata传值的方式: if formdata: try:
              #获取值
    if self.name in formdata: self.raw_data = formdata.getlist(self.name) else: self.raw_data = []
              #2.3.2.1 self.process_formdata(self.raw_data)

    第2.3.2.1步:

    class Field(object):    
        def process_formdata(self, valuelist):
            if valuelist:
                self.data = valuelist[0]   #赋值给self.data

    第3步:form.name 是如果在页面上显示出的 input 标签?

    print(form.name)    #form.name = simple.StringField 类的对象
    # 结果:<input class="form-control" id="name" name="name" type="text" value="">
    #我们看到打印的结果是 input 标签,其实form.name结果不一定是它,我们去simple.StringField类或其基类中找找__str__方法
    class Field(object):
        def __str__(self):
            return self()   #对象() ,执行__call__方法
    class Field(object):
        def __call__(self, **kwargs):
            return self.meta.render_field(self, kwargs)  #第2.1.1.1.1步可以看出 self.meta 是新创建Meta类对象, self 是simple.StringField类对象
    class DefaultMeta(object):
        def render_field(self, field, render_kw):
            #获取标签属性,field = simple.StringField类对象
            other_kw = getattr(field, 'render_kw', None)
            if other_kw is not None:
                render_kw = dict(other_kw, **render_kw)
                #{'class': 'form-control'}
                
            return field.widget(field, **render_kw)   
            # class StringField(Field):
          #    widget = widgets.TextInput() #widget 是一个TextInput类的对象
            # TextInput()() 执行__call__方法,去TextInput或基类中找
    class Input(object):
        def __call__(self, field, **kwargs):
            kwargs.setdefault('id', field.id)
            kwargs.setdefault('type', self.input_type)
         #如果render_kw中没有给value定义值
    if 'value' not in kwargs:
           #3.1步 kwargs[
    'value'] = field._value() #field 是 simple.StringField类对象
         #在这里,给input 标签添加属性,这样在页面上显示的标签就有了默认值
    return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))

     第3.1步

    class StringField(Field):
        def _value(self):
         #取self.data 中的值,返回
    return text_type(self.data) if self.data is not None else ''

     第4步:验证

    class Form(with_metaclass(FormMeta, BaseForm)):
        def validate(self):
            extra = {}
            for name in self._fields:      #{"name":simple.StringField类对象,"pwd":simple.PasswordField类对象}
                #到 LoginForm类中获取钩子函数
                inline = getattr(self.__class__, 'validate_%s' % name, None)
                if inline is not None:
                    #保存到extra字段中
                    extra[name] = [inline]
            #执行Form基类的validate方法
            return super(Form, self).validate(extra)
    class BaseForm(object):
        #self是LoginForm对象
        def validate(self, extra_validators=None):  #extra_validators是所有的钩子
            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()
                #4.1执行字段的validate方法
                if not field.validate(self, extra):
                    success = False
            return success

    第4.1步

    class Field(object):        
        def validate(self, form, extra_validators=tuple()):
            self.errors = list(self.process_errors)           #self.errors 是个列表,所以我们在前端页面上只显示一个
            stop_validation = False
            if not stop_validation:
           #把字段本身的校验规则和钩子规则放在一起 chain
    = itertools.chain(self.validators, extra_validators) #4.1.1 执行字段的_run_validation_chain stop_validation = self._run_validation_chain(form, chain) #如果校验未通过 stop_validation = True

           return len(self.errors) == 0

    第4.1.1步

    class Field(object):        
        def _run_validation_chain(self, form, validators):
            for validator in validators:
                try:
                    #validator是validators.DataRequired类对象
                    #4.1.1.1 对象()   调用__call__方法
                    validator(form, self) 
           #4.1.1.2 如果校验出异常
    except StopValidation as e: if e.args and e.args[0]:
                #在该字段的errors列表中添加错误信息 self.errors.append(e.args[0])
    return True except ValueError as e: self.errors.append(e.args[0]) return False

    第4.1.1.1步:分别执行各个的校验规则类的__call__方法和钩子函数

    class DataRequired(object):
        def __call__(self, form, field):
            #校验 该字段有数据并是字符串类型
            if not field.data or isinstance(field.data, string_types) and not field.data.strip():
                if self.message is None:
                    message = field.gettext('This field is required.')
                else:
                    message = self.message
    
                field.errors[:] = []
                #不然抛出异常
                raise StopValidation(message)
    class Length(object):
        def __call__(self, form, field):
            l = field.data and len(field.data) or 0
            if l < self.min or self.max != -1 and l > self.max:
                message = self.message
                if message is None:
                    if self.max == -1:
                        message = field.ngettext('Field must be at least %(min)d character long.',
                                                 'Field must be at least %(min)d characters long.', self.min)
                    elif self.min == -1:
                        message = field.ngettext('Field cannot be longer than %(max)d character.',
                                                 'Field cannot be longer than %(max)d characters.', self.max)
                    else:
                        message = field.gettext('Field must be between %(min)d and %(max)d characters long.')
    
                raise ValidationError(message % dict(min=self.min, max=self.max, length=l))
    class Regexp(object):
        def __call__(self, form, field, message=None):
            match = self.regex.match(field.data or '')
            if not match:
                if message is None:
                    if self.message is None:
                        message = field.gettext('Invalid input.')
                    else:
                        message = self.message
    
                raise ValidationError(message)
            return match
    def validate_pwd(self,*args,**kwargs):
            pass

    解释说明:

      如果校验成功,4.1.1.1步不抛出异常 →  4.1.1.2步不执行(self.errors没有值)→ 4.1.1步返回False → 4.1步返回True  → success=True  → 校验成功·

      如果校验不成功,每个字段的 .errors里都有错误信息,可以在前端页面上显示出来·

      注意:wtforms组件没有clean_data的概念,即使数据校验不成功,打印form.data也会打印出你输入的数据

    #print(form.data)
    class BaseForm(object):        
        @property
        def data(self):
            return dict((name, f.data) for name, f in iteritems(self._fields))

    最后我们也可以自己定义一个Form:

    from flask import Flask, render_template, request, redirect,Markup
    app = Flask(__name__, template_folder='templates')
    import wtforms
    app.debug = True
    
    # 插件
    class Widget(object):
        pass
    
    class InputText(Widget):
    
        def __call__(self, *args, **kwargs):
            return "<input type='text' name='name' />"
    
    class TextArea(Widget):
        def __call__(self, *args, **kwargs):
            return "<textarea name='email'> </textarea>"
    
    # Form
    class BaseForm(object):
        def __init__(self):
            # 获取当前字段
            _fields = {}
            for name,field in self.__class__.__dict__.items():
                if isinstance(field,Field):
                    _fields[name] = field
            self._fields = _fields
            self.data = {}
    
    
        def validate(self,request_data):
            # 找到所有的字段,执行每个字段的validate方法
            flag = True
            for name,field in self._fields.items():
                # 123
                input_val = request_data.get(name,'')
                result = field.validate(input_val)
                if not result:
                    flag = False
                else:
                    self.data[name] = input_val
            return flag
    # 字段
    class Field(object):
    
        def __str__(self):
            return Markup(self.widget())
    
    class StringField(Field):
        widget = InputText()
    
        def validate(self,val):
            if val:
                return True
    
    class EmailField(Field):
        widget = TextArea() # EmailField.widget/ self.widget
        reg = ".*@.*"
    
        def validate(self, val):
            import re
            if re.match(self.reg,val):
                return True
    
    # ############################ 使用 ###########################
    class LoginForm(BaseForm):
        name = StringField()
        email = EmailField()
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
    
        form = LoginForm()
        ret = form.validate(request.form)
        print("验证成功",ret)
        print("验证成功值",form.data)
        # print(obj.name)
        # print(obj.email)
        return render_template('login.html',form=form)
    
    if __name__ == '__main__':
        app.run()
    flask自定义Form
  • 相关阅读:
    hybrid项目h5页路由回退问题解决
    各个端类型判断
    图片展示方法总结
    微信小程序简易table组件实现
    微信小程序动画技巧
    git常用命令
    小程序跳转
    excel日期插件
    excel省市区三级分类级联
    mysql字符串分割操作
  • 原文地址:https://www.cnblogs.com/liuwei0824/p/8330985.html
Copyright © 2020-2023  润新知