zoukankan      html  css  js  c++  java
  • Flask--WTForms

    在说WTForms之前,我们先搞清楚Flask-WTF与WTForms的区别

    Flask-WTF

    基本了解

    Flask-WTF是集成WTForms,并带有 csrf 令牌的安全表单和全局的 csrf 保护的功能。
    每次我们在建立表单所创建的类都是继承与flask_wtf中的FlaskForm,而FlaskForm是继承WTForms中forms。

    用法:

    1.创建基础表单

    例如,form.py:

    class LoginForm(FlaskForm):
        username = StringField()
        password = PasswordField()
        remember_me = BooleanField(label='Keep me logged in')
    

    2.CSRF保护

    任何使用FlaskForm创建的表单发送请求,都会有CSRF的全部保护,在对应的template中HTML渲染表单时,可以加入form.csrf_token:

    <form method="post">
        {{ form.csrf_token }}
    </form>
    

    但是如果模板中没有表单,则可以使用一个隐藏的input标签加入csrf_token。

    <form method="post">
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    </form>
    

    3.验证表单

    在视图处理程序中验证请求:
    view.py

    def login():
        form = LoginForm()
        if form.validate_on_submit():
            return redirect('/success')
        return render_template('login.html', form=form)
    

    使用validate_on_submit 来检查是否是一个 POST 请求并且请求是否有效。

    4.文件上传

    Flask-WTF 提供 FileField 来处理文件上传,它在表单提交后,自动从 flask.request.files 中抽取数据。FileFielddata 属性是一个 Werkzeug FileStorage 实例。

    from werkzeug import secure_filename
    from flask_wtf.file import FileField
    
    class PhotoForm(Form):
        photo = FileField('Your photo')
    
    @app.route('/upload/', methods=('GET', 'POST'))
    def upload():
        form = PhotoForm()
        if form.validate_on_submit():
            filename = secure_filename(form.photo.data.filename)
            form.photo.data.save('uploads/' + filename)
        else:
            filename = None
        return render_template('upload.html', form=form, filename=filename)
    

    注意:在 HTML 表单的 enctype 设置成 multipart/form-data,如下:

    <form action="/upload/" method="POST" enctype="multipart/form-data">
        ....
    </form>
    

    5.验证码

    Flask-WTF 通过 RecaptchaField 也提供对验证码的支持:

    from flask_wtf import Form, RecaptchaField
    from wtforms import TextField
    
    class SignupForm(Form):
        username = TextField('Username')
        recaptcha = RecaptchaField()
    

    还需要配置一下信息:

    字段 配置
    RECAPTCHA_PUBLIC_KEY 必须 公钥
    RECAPTCHA_PRIVATE_KEY 必须 私钥
    RECAPTCHA_API_SERVER 可选 验证码 API 服务器
    RECAPTCHA_PARAMETERS 可选 一个 JavaScript(api.js)参数的字典
    RECAPTCHA_DATA_ATTRS 可选 一个数据属性项列表 https://developers.google.com/recaptcha/docs/display

    WTForms

    WTForms是一个Flask集成的框架,或者是说库。用于处理浏览器表单提交的数据。它在Flask-WTF 的基础上扩展并添加了一些随手即得的精巧的帮助函数,这些函数将会使在 Flask 里使用表单更加有趣。

    安装

    pip install wtforms

    WTForms支持的HTML标准字段

    字段类型 说明
    StringField 文本字段, 相当于type类型为text的input标签
    TextAreaField 多行文本字段
    PasswordField 密码文本字段
    HiddenField 隐藏文本字段
    DateField 文本字段, 值为datetime.date格式
    DateTimeField 文本字段, 值为datetime.datetime格式
    IntegerField 文本字段, 值为整数
    DecimalField 文本字段, 值为decimal.Decimal
    FloatField 文本字段, 值为浮点数
    BooleanField 复选框, 值为True 和 False
    RadioField 一组单选框
    SelectField 下拉列表
    SelectMultipleField 下拉列表, 可选择多个值
    FileField 文件上传字段
    SubmitField 表单提交按钮
    FormFiled 把表单作为字段嵌入另一个表单
    FieldList 子组指定类型的字段

    Validators验证器

    WTForms可以支持很多表单的验证函数:

    验证函数 说明
    Email 验证是电子邮件地址
    EqualTo 比较两个字段的值; 常用于要求输入两次密钥进行确认的情况
    IPAddress 验证IPv4网络地址
    Length 验证输入字符串的长度
    NumberRange 验证输入的值在数字范围内
    Optional 无输入值时跳过其它验证函数
    DataRequired 确保字段中有数据
    Regexp 使用正则表达式验证输入值
    URL 验证url
    AnyOf 确保输入值在可选值列表中
    NoneOf 确保输入值不在可选列表中

    自定义Validators验证器

    第一种: in-line validator(内联验证器)
    也就是自定义一个验证函数,在定义表单类的时候,在对应的字段中加入该函数进行认证。下面的my_length_check函数就是用于判name字段长度不能超过50.

    def my_length_check(form, field):
        if len(field.data) > 50:
            raise ValidationError('Field must be less than 50 characters')
    
    class MyForm(Form):
        name = StringField('Name', [InputRequired(), my_length_check])
    

    第二种:通用且可重用的验证函数
    一般是以validate开头,加上下划线再加上对应的field字段(validate_filed),浏览器在提交表单数据时,会自动识别对应字段所有的验证器,然后执行验证器进行判断。

    class RegistrationForm(FlaskForm):
        email = StringField('Email', validators=[DataRequired(), Length(1, 60), Email()])
        username = StringField('Username', validators=[DataRequired(), Length(1, 60),
            Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0, 'username must have only letters, numbers dots or underscores')])
        password = PasswordField('Password', validators=[DataRequired(), EqualTo('password2', message='password must match')])
        password2 = PasswordField('Confirm password', validators=[DataRequired()])
    
        def validate_email(self, field):
            if User.objects.filter(email=field.data).count() > 0:
                raise ValidationError('Email already registered')
    
        def validate_username(self, field):
            if User.objects.filter(username=field.data).count() > 0:
                raise ValidationError('Username has exist')
    

    第三种:比较高级的validators

    class Length(object):
        def __init__(self, min=-1, max=-1, message=None):
            self.min = min
            self.max = max
            if not message:
                message = u'Field must be between %i and %i characters long.' % (min, max)
            self.message = message
    
        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:
                raise ValidationError(self.message)
    
    length = Length
    

    Widget组件

    下面可以以登录界面为实例:

    #  login.py
    from flask import Flask, render_template, request, redirect
    from wtforms import Form
    from wtforms import validators
    from wtforms import widgets
    
    class LoginForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired(message='用户名不能为空.'),
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'}
        )
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.'),
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    

    使用

    案例一:登录

    from flask import Flask, render_template, request, redirect
    from wtforms.fields import simple
    from wtforms import validators, widgets, Form
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    
    class LoginForm(Form):
        # 字段(内部包含正则表达式)
        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'}
        )
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'GET':
            form = LoginForm()
            return render_template('login.html', form=form)
        else:
            form = LoginForm(formdata=request.form)
            if form.validate():
                print('用户提交数据通过格式验证,提交的值为:', form.data)
            else:
                print(form.errors)
            return render_template('login.html', form=form)
    
    if __name__ == '__main__':
        app.run()
    
    <!--login.html-->
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>登录</h1>
    <form method="post">
        <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
    
        <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    

    案例二:注册

    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 validators
    from wtforms import widgets
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    
    
    class RegisterForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired()
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'},
            default='cxw'
        )
    
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.')
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        pwd_confirm = simple.PasswordField(
            label='重复密码',
            validators=[
                validators.DataRequired(message='重复密码不能为空.'),
                validators.EqualTo('pwd', message="两次密码输入不一致")
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        email = html5.EmailField(
            label='邮箱',
            validators=[
                validators.DataRequired(message='邮箱不能为空.'),
                validators.Email(message='邮箱格式错误')
            ],
            widget=widgets.TextInput(input_type='email'),
            render_kw={'class': 'form-control'}
        )
    
        gender = core.RadioField(
            label='性别',
            choices=(
                (1, '男'),
                (2, '女'),
            ),
            #这句话的意思是上面的choices元组的第一个值是int类型
            #如果上上面为(‘1’, '男'),(‘2’, '女'),则下面的coerce则不用写
            coerce=int # “1” “2”
         )
        
        #这里是单选框
        city = core.SelectField(
            label='城市',
            choices=(
                ('bj', '北京'),
                ('sh', '上海'),
            )
        )
       
    	#这里是多选框
        hobby = core.SelectMultipleField(
            label='爱好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            coerce=int
        )
        
    	#这里是多选的checkbox
        favor = core.SelectMultipleField(
            label='喜好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            widget=widgets.ListWidget(prefix_label=False),
            option_widget=widgets.CheckboxInput(),
            coerce=int,
            default=[1, 2]
        )
        
    	#这里可以改值
        def __init__(self, *args, **kwargs):
            super(RegisterForm, self).__init__(*args, **kwargs)
            self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
    
        def validate_pwd_confirm(self, field):
            """
            自定义pwd_confirm字段规则,例:与pwd字段是否一致
            :param field:
            :return:
            """
            # 最开始初始化时,self.data中已经有所有的值
    
            if field.data != self.data['pwd']:
                # raise validators.ValidationError("密码不一致") # 继续后续验证
                raise validators.StopValidation("密码不一致")  # 不再继续后续验证
    
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        if request.method == 'GET':
            #这里可以传默认值
            form = RegisterForm(data={'gender': 2,'hobby':[1,]}) # initial
            return render_template('register.html', form=form)
        else:
            form = RegisterForm(formdata=request.form)
            if form.validate():
                print('用户提交数据通过格式验证,提交的值为:', form.data)
            else:
                print(form.errors)
            return render_template('register.html', form=form)
    
    
    
    if __name__ == '__main__':
        app.run()
    
    <!--register.html-->
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户注册</h1>
    <form method="post" novalidate style="padding:0  50px">
        {% for field in form %}
        <p>{{field.label}}: {{field}} {{field.errors[0] }}</p>
        {% endfor %}
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    
  • 相关阅读:
    分页存储过程
    WinForm中DataGridView显示更新数据--人性版
    char类型的说明
    代码创建数据库_表--SqlServer数据库
    单例设计模式
    c#中的正则表达式
    sessionStorage 和 localStorage
    图片懒加载插件lazyload.js详解
    git安装加操作(转)
    php获取数据转换成json格式
  • 原文地址:https://www.cnblogs.com/Hades123/p/11766285.html
Copyright © 2011-2022 走看看