zoukankan      html  css  js  c++  java
  • wtforms组件使用实例及源码解析

    WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

    WTforms作用:当网站中需要用到表单时,WTForms变得很有效。应该把表单定义为类,作为单独的一个模块。

    创建表单: 创建表单时,通常是创建一个Form的子类,表单的中的字段作为类的属性。

      1 from flask import Flask, render_template, request, redirect
      2 from wtforms import Form
      3 from wtforms.fields import core
      4 from wtforms.fields import html5
      5 from wtforms.fields import simple
      6 
      7 from wtforms import validators
      8 from wtforms import widgets
      9 
     10 app = Flask(__name__, template_folder='templates')
     11 app.debug = True
     12 
     13 class MyValidator(object): # 自定义验证器
     14     def __init__(self,message):
     15         self.message = message
     16 
     17     def __call__(self, form, field):
     18         # print(field.data)
     19         if field.data == '王浩':
     20             return None
     21         raise validators.StopValidation(self.message)
     22 
     23 
     24 class LoginForm(Form):
     25     name = simple.StringField(
     26         label='用户名',
     27         validators=[
     28             # MyValidator(message='用户名必须等于王浩')
     29             validators.DataRequired(message='用户名不能为空.'),
     30             validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
     31         ],
     32         widget=widgets.TextInput(),
     33         render_kw={'class': 'form-control'}
     34     )
     35     pwd = simple.PasswordField(
     36         label='密码',
     37         validators=[
     38             validators.DataRequired(message='密码不能为空.'),
     39             validators.Length(min=8, message='用户名长度必须大于%(min)d'),
     40             validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[$@$!%*?&])[A-Za-zd$@$!%*?&]{8,}",
     41                               message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
     42         ],
     43         widget=widgets.PasswordInput(),
     44         render_kw={'class': 'form-control'}
     45     )
     46 
     47 
     48 @app.route('/login', methods=['GET', 'POST'])
     49 def login():
     50     if request.method == 'GET':
     51         form = LoginForm()
     52         return render_template('login.html', form=form)
     53     else:
     54         form = LoginForm(formdata=request.form)
     55         if form.validate():
     56             print('用户提交数据通过格式验证,提交的值为:', form.data)
     57         else:
     58             print(form.errors)
     59         return render_template('login.html', form=form)
     60 
     61 
     62 # ########################### 用户注册 ##########################
     63 class RegisterForm(Form):
     64     name = simple.StringField(
     65         label='用户名',
     66         validators=[
     67             validators.DataRequired()
     68         ],
     69         widget=widgets.TextInput(),
     70         render_kw={'class': 'form-control'},
     71         default='alex'
     72     )
     73 
     74     pwd = simple.PasswordField(
     75         label='密码',
     76         validators=[
     77             validators.DataRequired(message='密码不能为空.')
     78         ],
     79         widget=widgets.PasswordInput(),
     80         render_kw={'class': 'form-control'}
     81     )
     82 
     83     pwd_confirm = simple.PasswordField(
     84         label='重复密码',
     85         validators=[
     86             validators.DataRequired(message='重复密码不能为空.'),
     87             validators.EqualTo('pwd', message="两次密码输入不一致")
     88         ],
     89         widget=widgets.PasswordInput(),
     90         render_kw={'class': 'form-control'}
     91     )
     92 
     93     email = html5.EmailField(
     94         label='邮箱',
     95         validators=[
     96             validators.DataRequired(message='邮箱不能为空.'),
     97             validators.Email(message='邮箱格式错误')
     98         ],
     99         widget=widgets.TextInput(input_type='email'),
    100         render_kw={'class': 'form-control'}
    101     )
    102 
    103     gender = core.RadioField(
    104         label='性别',
    105         choices=(
    106             (1, ''),
    107             (2, ''),
    108         ),
    109         coerce=int
    110     )
    111     city = core.SelectField(
    112         label='城市',
    113         choices=(
    114             ('bj', '北京'),
    115             ('sh', '上海'),
    116         )
    117     )
    118 
    119     hobby = core.SelectMultipleField(
    120         label='爱好',
    121         choices=(
    122             (1, '篮球'),
    123             (2, '足球'),
    124         ),
    125         coerce=int
    126     )
    127 
    128     favor = core.SelectMultipleField(
    129         label='喜好',
    130         choices=(
    131             (1, '篮球'),
    132             (2, '足球'),
    133         ),
    134         widget=widgets.ListWidget(prefix_label=False),
    135         option_widget=widgets.CheckboxInput(),
    136         coerce=int,
    137         default=[1, 2]
    138     )
    139 
    140     def __init__(self, *args, **kwargs):
    141         super(RegisterForm, self).__init__(*args, **kwargs)
    142         self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
    143 
    144     def validate_pwd_confirm(self, field):
    145         """
    146         自定义pwd_confirm字段规则,例:与pwd字段是否一致
    147         :param field:
    148         :return:
    149         """
    150         # 最开始初始化时,self.data中已经有所有的值
    151 
    152         if field.data != self.data['pwd']:
    153             # raise validators.ValidationError("密码不一致") # 继续后续验证
    154             raise validators.StopValidation("密码不一致")  # 不再继续后续验证
    155 
    156 
    157 @app.route('/register', methods=['GET', 'POST'])
    158 def register():
    159     if request.method == 'GET':
    160         # 设置默认值
    161         form = RegisterForm(data={'gender': 1})
    162         return render_template('register.html', form=form)
    163     else:
    164         form = RegisterForm(formdata=request.form)
    165         if form.validate():
    166             print('用户提交数据通过格式验证,提交的值为:', form.data)
    167         else:
    168             print(form.errors)
    169         return render_template('register.html', form=form)
    170 
    171 
    172 if __name__ == '__main__':
    173     app.run()
    注册登录页面自定义表单
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6 </head>
     7 <body>
     8 <h1>用户注册</h1>
     9 <form method="post" novalidate style="padding:0  50px">
    10     {% for item in form %}
    11     <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
    12     {% endfor %}
    13     <input type="submit" value="提交">
    14 </form>
    15 </body>
    16 </html>
    注册.html

    源码分析

    将按照代码的执行顺序分析

    1.当定义好一个自定义的Form类,项目加载Form类所在模块,代码都做了什么?

    class RegisterForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired()
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'},
            # default='alex'
        )
    
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.')
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )

    自定义类继承了Form类,看一下Form类

    class Form(with_metaclass(FormMeta, BaseForm)):
        pass
    
    
    def with_metaclass(meta, base=object):
        return meta("NewBase", (base,), {})
    
    # 那么相当于Form继承了FormMeta("NewBase",(BaseForm,),{} ) 使用 FormMeta元类创建的对象让Form继承,则Form的元类也被指定为FormMeta
    
    class FormMeta(type):
        def __init__(cls, name, bases, attrs):
            type.__init__(cls, name, bases, attrs)
            cls._unbound_fields = None
            cls._wtforms_meta = None

    则 FormMeta创建RegisterForm为了RegisterForm产生了两个类变量

    RegisterForm._unbound_fields = None
    RegisterForm._wtforms_meta = None

    再看类变量name 和 pwd 的字段是什么

    class StringField(Field):
        """
        This field is the base for most of the more complicated fields, and
        represents an ``<input type="text">``.
        """
        widget = widgets.TextInput()
    
        def process_formdata(self, valuelist):
            if valuelist:
                self.data = valuelist[0]
            elif self.data is None:
                self.data = ''
    
        def _value(self):
            return text_type(self.data) if self.data is not None else ''
        
    class Field(object):
        """
        Field base class
        """
        errors = tuple()
        process_errors = tuple()
        raw_data = None
        validators = tuple()
        widget = None
        _formfield = True
        _translations = DummyTranslations()
        do_not_call_in_templates = True  # Allow Django 1.4 traversal
    
        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)
    
        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):
            pass
    StringField源码

    此时RegisterForm的类变量为

    RegisterForm._unbound_fields = None
    RegisterForm._wtforms_meta = None
    RegisterForm.name = UnboundField(simple.StringField)
    RegisterForm.pwd = UnboundField(simple.PasswordField)
    print(RegisterForm.name,type(RegisterForm.name))
    “”“
    <UnboundField(StringField, (), {'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x000001C76FE778D0>], 

    'widget': <wtforms.widgets.core.TextInput object at 0x000001C76FE77B00>, 'render_kw': {'class': 'form-control'}, 'default': 'ppp'})> <class 'wtforms.fields.core.UnboundField'> ”“”
    class UnboundField(object):
        _formfield = True
        creation_counter = 0 # 初始值为0 
    
        def __init__(self, field_class, *args, **kwargs):
            UnboundField.creation_counter += 1 # 每事实例化一次,类变量creation_counter +1 
            self.field_class = field_class
            self.args = args
            self.kwargs = kwargs
            self.creation_counter = UnboundField.creation_counter # 实例自己的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)
    UnboundField源码

    所以可知

    RegisterForm._unbound_fields = None
    RegisterForm._wtforms_meta = None
    RegisterForm.name = UnboundField(creation_counter=1,simple.StringField)
    RegisterForm.pwd = UnboundField(creation_counter=,2,simple.PasswordField)

    2.接着在视图函数中RegisterForm中实例化发生了什么?

    补充:dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

     1 class A:
     2     x = 'xxx'
     3     age = 111
     4     def __init__(self,name):
     5         self.name = 'cp'
     6 
     7     def say_hi(self):
     8         print('hi')
     9 
    10 print(dir(A))
    11 a = A('cp')
    12 print("="*40)
    13 print(dir(a))
    14 """
    15 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',  
    16 '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',  
    17 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',  '__subclasshook__', '__weakref__', 'age', 'say_hi', 'x'
    18 ]
    19 ========================================
    20 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',  
    21  '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',  
    22  '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_hi', 'x'
    23  ]
    24 """
    dir示例

    执行 form = RegisterForm(data={'gender': 1})

    # 因为RegisterForm指定了FormMeta为元类,则()触发其__call__方法
    def __call__(cls, *args, **kwargs):
        if cls._unbound_fields is None:
            fields = []
            for name in dir(cls): # 遍历RegisterForm的成员 name为str
                if not name.startswith('_'): # 可知自定义表单类中字段名不能以'_'开头
                    unbound_field = getattr(cls, name) 
                    if hasattr(unbound_field, '_formfield'): # _formfield为UnboundField的类变量 默认True 如果有x=123 则为False
                        fields.append((name, unbound_field))
            # fields = [
            #    (name, UnboundField(creation_counter=1,simple.StringField))
            #    (pwd, UnboundField(creation_counter=2,simple.PasswordField))
            # ]
            # 利用creation_counter序列编号按前后代码中的字段顺序排序
            fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
            cls._unbound_fields = fields  # 此时_unbound_fields的初始值None被覆盖了
       
      if cls._wtforms_meta is None:
    bases = []
    for mro_class in cls.__mro__: # 从RegisterForm的mro列表中遍历
    if 'Meta' in mro_class.__dict__: # 查看是否有写去过Meta类
    bases.append(mro_class.Meta) # 只在Form中找到 Meta = DefaultMeta
       # 相当于 type('Meta', tuple(DefaultMeta), {})
           cls._wtforms_meta = type('Meta', tuple(bases), {}) 
    return type.__call__(cls, *args, **kwargs)

    接着执行: type.__call__(cls, *args, **kwargs)  

    因为RegisterForm及其父类没有写__new__方法,则调用object的__new__创建对象,__init__则查找到父类Form中的。

     1 class Form(with_metaclass(FormMeta, BaseForm)):
     2     def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
     3         meta_obj = self._wtforms_meta()
     4         if meta is not None and isinstance(meta, dict):
     5             meta_obj.update_values(meta)
     6         super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix) # 调用父类的方法
     7 
     8 
     9         for name, field in iteritems(self._fields): # 上一步给self._fields赋好值后,循环
    10             setattr(self, name, field) # 给self.name = simple.StringField()  self.pwd = simple.PasswordField()
    11             # 就可以 form.name form.pwd 了
    12         self.process(formdata, obj, data=data, **kwargs) # BaseForm的方法
    13 
    14 
    15 class BaseForm(object):
    16     def __init__(self, fields, prefix='', meta=DefaultMeta()):
    17         if prefix and prefix[-1] not in '-_;:/.':
    18             prefix += '-'
    19 
    20         self.meta = meta
    21         self._prefix = prefix
    22         self._errors = None
    23         self._fields = OrderedDict()
    24 
    25         if hasattr(fields, 'items'):
    26             fields = fields.items()
    27 
    28         translations = self._get_translations()
    29         extra_fields = []
    30         if meta.csrf:
    31             self._csrf = meta.build_csrf(self)
    32             extra_fields.extend(self._csrf.setup_form(self))
    33 
    34         # fields = _unbound_fields = [
    35         #    (name, UnboundField(creation_counter=1,simple.StringField))
    36         #    (pwd, UnboundField(creation_counter=2,simple.PasswordField))
    37         # ]
    38         for name, unbound_field in itertools.chain(fields, extra_fields):
    39             options = dict(name=name, prefix=prefix, translations=translations)
    40             field = meta.bind_field(self, unbound_field, options) # 实例化simple.StringField
    41             self._fields[name] = field # OrderedDict()向有序字典中添加排好序的fields
    42 
    43         #  self._fields = OrderedDict = {
    44         #   name: simple.StringField(),
    45         #   pwd: simple.PasswordField(),
    46         # }
    47 
    48     def process(self, formdata=None, obj=None, data=None, **kwargs):
    49         """
    50         Take form, object data, and keyword arg input and have the fields
    51         process them.
    52         。。。
    53         """
    54         formdata = self.meta.wrap_formdata(self, formdata)
    55 
    56         if data is not None:
    57             # XXX we want to eventually process 'data' as a new entity.
    58             #     Temporarily, this can simply be merged with kwargs.
    59             kwargs = dict(data, **kwargs)
    60 
    61         for name, field, in iteritems(self._fields):
    62             if obj is not None and hasattr(obj, name):
    63                 field.process(formdata, getattr(obj, name))
    64             elif name in kwargs:
    65                 field.process(formdata, kwargs[name])
    66             else:
    67                 field.process(formdata)
    type.__call__执行

     从源码中可知使用注意:

    1、字段名是区分大小写的

    2、字段名不能以'_'开头

    3、字段名不能以'validate'开头

    参考:

    1.https://www.cnblogs.com/Chuck-Y/p/8260156.html?utm_source=tuicool&utm_medium=referral

    2.https://www.cnblogs.com/wupeiqi/articles/8202357.html

  • 相关阅读:
    OSCP Learning Notes Buffer Overflows(3)
    OSCP Learning Notes Buffer Overflows(5)
    OSCP Learning Notes Exploit(3)
    OSCP Learning Notes Exploit(4)
    OSCP Learning Notes Exploit(1)
    OSCP Learning Notes Netcat
    OSCP Learning Notes Buffer Overflows(4)
    OSCP Learning Notes Buffer Overflows(1)
    OSCP Learning Notes Exploit(2)
    C++格式化输出 Learner
  • 原文地址:https://www.cnblogs.com/carlous/p/10598108.html
Copyright © 2011-2022 走看看