• Flask 表单以及表单验证


    表单

    说到表单,在HTML中表单的创建时通过

    标签实现的,在标签内部,字段通过使用标签等定义。比如一个表单内部有用户名,密码框这些,都是通过标签等实现的。

    一个简单的表单:
    在这里插入图片描述

    <form>
    First name:<br>
    <input type="text" name="firstname">
    <br>
    Last name:<br>
    <input type="text" name="lastname">
    form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将表单提交给服务端处理时,服务端需要验证表单中的字段的取值是否符合要求。

    在 Python 的 Web 开发中,表单内部的字段标签之类的,就不再使用HTML标签来写了,而是通过python类实现。

    WTForms在python中使用类定义表单,然后直接通过类定义生成对应的HTML代码,这种方式更加方便,而且使表单更易于重用。除非是非常简单的程序,或者想让表单的定义更加灵活,否则一般不会在模板中直接使用HTML代码写表单。

    WTForms 和 Flask-WTF

    WTForms 是一个灵活的表单验证和表单渲染的库,它的主要功能:

    • 验证表单中的字段的取值是否符合要求;
    • 渲染输出表单的 HTML 代码

    WTForms 可以与任意的 Web 框架和模板引擎一起使用。 在 Flask 框架或者 Django 框架中,都可以使用 WTForms。

    Flask-WTF 在 WTForms 的基础上提供了一些扩展,可以方便的在 Flask 框架中生成表单。

    创建表单

    下面直接用代码讲解如何创建表单。

    首先,我们的目标是这样一个页面
    在这里插入图片描述
    一个简单的登录页面,让用户输入用户名,密码,同时对用户名和密码进行验证,验证成功就输出,验证不成功需要重新输入,并提示错误信息。

    首先,这是一个表单,表单内有一些文本,有文本框,有登录按钮,这些都是通过python中的表单类创建的,使用的正是flask-wtf模块创建的。

    先导入需要的模块

    #导入表单类
    from flask_wtf import FlaskForm
    #导入字段类
    from wtforms import StringField,SubmitField,PasswordField
    #导入验证器类
    from wtforms.validators import DataRequired,Length,Regexp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    class LoginForm(FlaskForm):
        '''
        这些字段对象会被渲染为html标签
        实例化一个文本字段对象,设置参数
        label:这个值可以拿出来放在标签前面显示
        validators=[]验证器列表,实例化的验证器对象,里面又可以设置参数
    
        '''
        #用户名字段
        username = StringField(
            label='用户名',
            validators=[
                DataRequired(message='用户名不能为空'),
                Length(3,20,message='用户名长度在3-20个字符')
            ]
        )
    
        password = PasswordField(
            label='密码',
            validators=[
                DataRequired(message='密码不能为空'),
                Length(3,20,message='密码长度在3-20个字符'),
     			Regexp(r'^[a-z0-9A-Z]+$',message='密码只能有字母,数字,下划线组成')
            ]
        )
    
        submit = SubmitField(label='登录')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    我们创建了一个类LoginForm,它是一个表单类,继承了FlaskForm,这个表单中包含一个用户名username字段,密码password字段,还有一个提交按钮submit,这三个字段作为了类LoginForm的三个属性。

    表单字段

    我们发现用户名,密码这些实际是文本框,既然不用HTML代码实现,那么怎么实现呢?

    使用的就是WTForms中的各种字段类,比如这里:

    username = StringField()
    
    • 1

    StringField()是WTForms 支持表单字段类,它表示文本字段,这样一个字段会被渲染为

    <input id="username" name="username" type="text" value="">
    
    • 1

    注意:实例化对象的名称是username ,被渲染后的input标签中的idname属性值都叫username

    WTForms 支持如下类型的表单字段:
    在这里插入图片描述
    这只是一部分,还有很多其他支持的字段。

    接着看代码:

        username = StringField(
            label='用户名',
            validators=[
                DataRequired(message='用户名不能为空'),
                Length(3,20,message='用户名长度在3-20个字符')
            ]
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    验证器

    StringField类需要传递一些参数:

    label='用户名' ,这个参数可以直接在模板页面中显示,使用form.username.label,把它放在文本框的前面,像下面这样:
    在这里插入图片描述

    validators=[ ] 这个参数表示对这个字段使用的验证器列表。验证器就是对这个字段使用那些验证,比如这里DataRequired,Length两个验证器类,同样验证器类需要实例化然后传入参数。比如:

     Length(3,20,message='用户名长度在3-20个字符')
    
    • 1

    限定长度在3-20字符,如果验证不通过,就弹出错误信息:message='用户名长度在6-20个字符'

    验证器也有很多类型,常见的验证器类有:
    在这里插入图片描述
    还可以自定义验证器类等等。

    我们创建了一个单独的文件loginform.py

    from flask_wtf import FlaskForm
    from wtforms import StringField,PasswordField,SubmitField
    from wtforms.validators import DataRequired,Regexp,Length
    
    class LoginForm(FlaskForm):
        #
        username = StringField(
            label='用户名',
            validators=[
                DataRequired(message='用户名不能为空'),
                Length(3,20,message='用户名长度在3-20个字符')
            ]
        )
    
        password = PasswordField(
            label='密码',
            validators=[
                DataRequired(message='密码不能为空'),
                Length(3,20,message='密码长度在3-20个字符'),
                Regexp(r'^[a-z0-9A-Z]+$',message='密码只能有字母,数字,下划线组成')
            ]
        )
    
        submit = SubmitField(label='登录')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    表单类创建好之后,可以做一个简单的测试:
    把这个表单显示出来

    首先,我们在app.py文件中:

    from flask import Flask,render_template,request
    from loginform import LoginForm
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'hard to guess string'
    
    @app.route('/',methods=['GET','POST'])
    def login():
        loginform = LoginForm()
        if request.method == 'GET':
            return render_template('login.html',form = loginform)
    
        return 'hello'
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    app.config['SECRET_KEY'] = 'hard to guess string'
    用于防范 CSRF 攻击,具体的我也不清楚,就不再细说了,总之需要设置
    
    • 1
    • 2

    接着,在根路由下/,有视图函数login,访问这个路由的方法可以是GET,或者POST。首先假设是GET,我们就需要把之前的表单渲染出来,具体的做法就是:

    使用创建的表单类LoginForm实例化一个表单对象loginform ,如果请求方法是GET,就去渲染login.html,然后表单对象作为参数传递过去。所以我们还需要设计一下login.html文件。

    login.html:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面title>
    head>
    <body>
    
    <form action="{{ url_for('login') }}" method="post">
        <table>
            <tbody>
            <tr>
                <td>{{ form.username.label }}td>
                <td>{{ form.username }}td>
                <td>{% for err in form.username.errors %}
                    {{ err }}
                    {% endfor %}
                td>
            <td>{{ form.test }}td>
            tr>
            <tr>
                <td>{{ form.password.label }}td>
                <td>{{ form.password }}td>
                <td>{% for err in form.password.errors %}
                    {{ err }}
                    {% endfor %}
                td>
            tr>
            <tr>
                <td>{{ form.submit }}td>
            tr>
            {{ form.hidden_tag() }}
            tbody>
        table>
    form>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    这里使用了一个表格布局,然后表单的action="{{ url_for('login') }}" method="post",设置了表单的提交路径,以及提交方法。

        {{ form.username.label }}#拿到username的label值显示出来
        {{ form.username }}#显示username这个字段
        {% for err in form.username.errors %}#这是错误信息,如果验证不通过会显示错误信息
            {{ err }}
            {% endfor %}
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最后,还要注意到: {{ form.hidden_tag() }}, 用于防范 CSRF 攻击,生成 标签。总之必须要加上,不加上,后面验证数据的时候会出问题。

    接着,运行app.py,访问根路径/,则是GET方法访问,会渲染出login.html文件。
    在这里插入图片描述

    接着,我们需要实现,用户输入数据,我们进行验证,验证不成功就提示错误信息,验证成功就登录成功。

    回到表单类中,我们可以发现使用的验证器有这几种:
    在这里插入图片描述

    其中,DataRequired,Length直接被渲染为HTML中input标签的属性:

    maxlength="" minlength="" required
    
    • 1

    在这里插入图片描述
    这时候验证就是在浏览器上完成的了,而不是在服务器。客户端方式可以实时动态的提示用户输入是否正确,只有用户输入正确后才会将表单数据发送给服务器。客户端验证可以增强用户体验,降低服务器负载。

    参考书上说,Flask程序中使用WTForms实现的就是服务器验证,但是有些字段又会被渲染为HTML5的属性,所以也不全是服务器验证。

    这就是浏览器给出的验证提示
    在这里插入图片描述

    但是正则验证器,确实是在服务端完成验证的

    Regexp(r'^[a-z0-9A-Z]+$',message='密码只能有字母,数字,下划线组成')
    
    • 1

    那么怎么在服务器验证数据呢,首先,我们的数据会提交到/路径,使用的是POST方法。

    表单类对象loginform,有一个验证方法,loginform.validate(),调用这个方法时,会去调用你每一个字段中使用的每一个验证器去验证数据,全部验证通过返回True,失败返回False。

    @app.route('/',methods=['GET','POST'])
    def login():
        loginform = LoginForm()
        if request.method == 'GET':
            return render_template('login.html',form = loginform)
        elif request.method == 'POST' and loginform.validate():
            return f'用户 {loginform.username.data}登录成功'
        else:
            return render_template('login.html',form = loginform)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 如果请求方法时GET,直接渲染表单
    • 如果请求方法时POST,并且
      loginform.validate()==True,表示验证通过,这时候我们返回登录成功,同时使用loginform.username.data可以拿到用户输入的用户名。
    • 否则,验证失败,再次渲染这个表单,注意:这时候,调用了validate()方法,返回False,产生了错误信息,错误信息在form.username.errors,是一个列表,这时候login.html中的错误信息列表就是有输出了。
      在这里插入图片描述

    测试一下:我的密码字段设置了正则验证,只允许输入字母数字下划线,而且字符个数在3-20个。

    首先,不填写字段,浏览器端会自动验证
    在这里插入图片描述

    输入正确格式的密码:
    在这里插入图片描述

    输入错误格式的密码:
    在这里插入图片描述

  • 相关阅读:
    【python】内置函数汇总
    讲解神经网络的书籍,讲解神经网络的软件
    Deep Image Matting:深度学习Matting开山之作
    畅购商城_第12章_分布式事务解决方案
    关于spring项目中,security 对websocket请求和拦截问题
    如何正确地配置Gradle版本
    win10安装 nvm + angular
    JS 字符串转 GBK 编码超精简实现
    2分能出线,6分却不能出线?世界杯小组赛的出线规则这次真被我整明白了
    Python 1-06 练习
  • 原文地址:https://blog.csdn.net/weixin_42576837/article/details/126404406