zoukankan      html  css  js  c++  java
  • Flask WTForms的使用和源码分析 —— (7)

     

     Flask-WTF是简化了WTForms操作的一个第三方库。WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。还有其它一些功能:CSRF保护,

    文件上传等。安装方法:

    1
    pip3 install flask-wtf

    用户登录注册示例

     1. 用户登录

      当用户登录时候,需要对用户提交的用户名和密码进行多种格式校验。如:

      用户不能为空;用户长度必须大于6;

      密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);

     app.py
     templates/login.html

    2. 用户注册

      注册页面需要让用户输入:用户名、密码、密码重复、性别、爱好等。

     app01.py
     templates/register.html

     

    源码分析

    流程图

    类的创建过程分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class LoginForm(Form):
        # 字段(内部包含正则表达式)
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired(message='用户名不能为空.'),
                validators.Length(min=6max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
            ],
            widget=widgets.TextInput(), # 页面上显示的插件
            render_kw={'class''form-control'}
        )

    LoginForm继承Form 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Form(with_metaclass(FormMeta, BaseForm)):
        """
        Declarative Form base class. Extends BaseForm's core behaviour allowing
        fields to be defined on Form subclasses as class attributes.
     
        In addition, form and instance input data are taken at construction time
        and passed to `process()`.
        """
        Meta = DefaultMeta
    def with_metaclass(meta, base=object):
        return meta("NewBase", (base,), {})

    我们可以看到  with_metaclass 通过FormMeta(继承type) 动态的创建了一个类,这时候会执行 FormMeta的__init__方法

    复制代码
    class FormMeta(type):
      # cls是LoginForm类
    def __init__(cls, name, bases, attrs): type.__init__(cls, name, bases, attrs) cls._unbound_fields = None cls._wtforms_meta = None
    复制代码

    通过以上类的创建,我们自定义的类多了两个属性

    1
    2
    LoginForm._unbound_fields = None
    LoginForm._wtforms_meta = None  

    对LoginForm中的字段 name 进行追踪

    复制代码
    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'}
        )
    复制代码

    我们可以看到name是一个对象,先追踪其__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)

    通过上面的源码解读,我们知道由于属性没有_form和_name所以其返回的是 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
    复制代码

    结果

    LoginForm.name = UnboundField(simple.StringField,StringField的所有参数)LoginForm.pwd = UnboundField(simple.PasswordField,PasswordField的所有参数)

    自动生成HTML

    前端调用LoginForm.name如何生成input框的呢,首先我们可以看出LoginForm中的name是一个对象

    复制代码
    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 ''
    复制代码

    执行LoginForm.name会触发其内部的__str__方法

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

    我们可以看到返回的是自己,会触发其__call__方法

    1
    2
    def __call__(self**kwargs):
        return self.meta.render_field(self, kwargs)

    render_field方法如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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)

    widget是一个对象,会触发其内部的__call__方法

    复制代码
    class StringField(Field):
        """
        This field is the base for most of the more complicated fields, and
        represents an ``<input type="text">``.
        """
        widget = widgets.TextInput()
    复制代码

    TextInput对象  

    1
    2
    3
    4
    5
    class TextInput(Input):
        """
        Render a single-line text input.
        """
        input_type = 'text'

    其__call__方法如下所示  

    1
    2
    3
    4
    5
    6
    7
    8
    def __call__(self, field, **kwargs):
        kwargs.setdefault('id', field.id)
        kwargs.setdefault('type'self.input_type)
        if 'value' not in kwargs:
            kwargs['value'= field._value()
        if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
            kwargs['required'= True
        return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
  • 相关阅读:
    智器SmartQ T7实体店试用体验
    BI笔记之SSAS库Process的几种方案
    PowerTip of the Day from powershell.com上周汇总(八)
    PowerTip of the Day2010071420100716 summary
    PowerTip of the Day from powershell.com上周汇总(十)
    PowerTip of the Day from powershell.com上周汇总(六)
    重新整理Cellset转Datatable
    自动加密web.config配置节批处理
    与DotNet数据对象结合的自定义数据对象设计 (二) 数据集合与DataTable
    在VS2003中以ClassLibrary工程的方式管理Web工程.
  • 原文地址:https://www.cnblogs.com/fengff/p/12421154.html
Copyright © 2011-2022 走看看