乐趣区

第三天在Flask-Web中如何使用表单

原文:http://www.catonlinepy.tech/
声明:原创不易,未经许可,不得转载

1. 你将学会什么

通过第三天的学习内容,你将对表单有所了解。知道使用插件来处理应用中的表单,以后遇到表单能够更熟练的使用 Flask_WTF 插件,并完美的将应用运行起来。今天的学习内容涉及到的代码都会托管到 github 上,在学习本课内容时,一定要自己尝试手敲代码,遇到问题再到猫姐的 github 上去查看代码,如果实在不知道如何去解决问题,可以在日志下面留言说明具体情况。

2. 表单的插件简介

WTForms 作为处理 Web 表单的插件,是一款支持多个 web 框架的 form 组件。Flask-WTF 插件对其类 WTForms 进行封装后以便它能够与 Flask 完美的结合。在第三天的内容中,我们将引入第一个 Flask 插件,后续也会对其它的插件进行引入。要知道,插件用得好,可以使 web 的开发的过程快到飞起。

大家需知道,所有 Flask 插件都是属于 Python 的三方包,因此都可以使用 pip 来进行安装。照旧,我们先进入 miao_venv 的虚拟环境目录中,将虚拟环境进行激活,然后安装 Flask_WTF 插件。安装步骤如下:

# 进入到虚拟环境目录,激活虚拟环境
maojie@Thinkpad:~/flask-plan/$ source miao_venv/bin/activate

# 再安装 Flask_WTF 插件
(miao_venv) maojie@Thinkpad:~/flask-plan$ pip install Flask_WTF

3. 表单插件的使用

今天的代码组织结构如下:

# 使用 tree 命令查看
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ tree
.
├── form_demo
│   ├── __init__.py
│   ├── routes.py
│   └── templates
│       ├── form.html
│       └── layout.html
└── run.py

现在猫姐来创建今天的课程目录,步骤如下:

# 在 flask-course-primary 目录下创建第三天的课程 day3 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day3

# 进入 day3 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day3

# 新建 form_demo
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ mkdir form_demo

# 进入到 form_demo 包
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ cd form_demo/

# 在 form_demo 包中新建__init__.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo$ touch __init__.py

# 在 form_demo 新建 routes.py 路由文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo$ touch routes.py

# 在 day3 目录下新建 run.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/$ touch run.py

上面将课程目录创建好后,现在我们开始表单的创建。在创建表单之前,猫姐先在__init__.py 文件中对 Flask_WTF 进行配置,如下所示:

# __init__.py 文件中的内容
from flask import Flask    # 从 flask 包中导入 Flask 类

app = Flask(__name__)      # 通过 Flask 类创建一个 app 实例

app.config['SECRET_KEY'] = 'miaojie is great!'  # 对 Flask_WTF 进行配置

from form_demo import routes

# 解释:对 Flask_WTF 进行配置主要是为了防跨站请求伪造保护。因为在默认的情况下,Flask_WTF 能够保护所有的表单免受跨站请求伪造的攻击(Cross-Site Request Forgery,CSRF),但是在特殊情况下,一些恶意网站会把请求发送到被攻击者已登录的其它网站时就会发生 CSRF 攻击。为了实现 CSRF 的保护,Flask_WTF 需要程序设置一个密钥,然后它使用密钥去生成一个加密令牌,再用令牌验证请求中的表单数据的真伪。(这里不懂也没事,问题不大!)

下面开始一个简单的 Web 表单,猫姐开始利用 Flask_WTF 插件来创建 Web 表单,表单内容写入到 routes.py 文件中,如下所示:

# routes.py 文件中的内容
# 从 flask_wtf 库中导入 FlaskForm 类
from flask_wtf import FlaskForm

# 从 wtforms 中导入表单的字段对象类      
from wtforms import StringField, PasswordField, BooleanField, SubmitField 

# 从 wtforms.validators 字段验证器中导入 DataRequired 验证函数 
from wtforms.validators import DataRequired    

# LoginForm 类函数,继承自 FlaskForm 基类
class LoginForm(FlaskForm):
     # 表单包括用户名,密码和是否记住密码及一个提交按钮
     username = StringField('Username', validators=[DataRequired()])
     password = PasswordField('Password', validators=[DataRequired()])
     remember = BooleanField('Remember Me')
     submit = SubmitField('Sign In')

由于只需要用户输入用户名、密码、记住登录状态以及提交按钮,所以表示 web 表单的 LoginForm 仅包含了用户名,密码和是否记住密码可选框以及提交按钮这 4 个字段。其中,StringField 类表示属性为 type=”text” 的 <input> 元素;PasswordField 类表示属性为 type=”password” 的 <input> 元素;BooleanField 表示为 true 或 false 的可选框;SubmiteField 表示属性为 type=”submit” 的 <input> 元素,而 Username,Password,Remember Me 表示标签名称(label)。在字段类中的可选参数—validators—是一个 list 变量,可以向类填充不同的校验函数,它在用户提交表单按钮前验证数据,这里仅仅只验证了字段输入是否为空。

接下来是表单在前端的渲染,将表单内容添加到模板中,让浏览器进行渲染,用户在网页中就可以看到表单了。下面开始进行模板的创建,如下所示:

# 在 form_demo 中创建 templates 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo$ mkdir templates

# 进入 templates 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo/$ cd templates

# 创建基模板 layout.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo/templates$ touch layout.html

# 创建子模板 form.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo/templates$ touch form.html

在第二课中猫姐已经讲到了模板的继承,在今天的课程中还是用到继承这个概念。在 templates/layout.html 中写入如下代码:

<!-- layou.html 文件中的内容 -->
<html>
    <head>
      {% if title %}
        <title>{{title}} - 表单的使用 - 喵星在线 </title>
      {% else %}
        <title> 表单的使用 - 喵星在线 </title>
      {% endif %}
    </head>
    <body>
       {% block content %}
       {% endblock %}
    </body>
</html>

然后子模板继承基模板,在 templates/form.html 中写入如下代码:

<!-- form.html 文件中的内容 -->
{% extends "layout.html" %}

{% block content %}
    <h1>Sign In</h1>
    <form action=""method="post">
            # 防跨站伪造请求  
            {{html_form.hidden_tag() }}
        <div>
            # 显示 Username 标签名称 
            {{html_form.username.label}}
            # Username 输入框 
            {{html_form.username() }}
        </div>
         <div>
            # 显示 Password 标签名称
            {{html_form.password.label}}
            # 密码输入框
            {{html_form.password() }}
        </div>
        <div>
            # 显示 Remember Me 标称名称
            {{html_form.remember.label}}
            # 是否勾选记住密码框
            {{html_form.remember() }}
        </div>
        <div>
            # 显示提交按钮
            {{html_form.submit() }}
        </div>
    </form>
{% endblock content %}

通过 <form></form> 创建表单,它的 action 属性告诉浏览器用户在提交表单时将发送请求的 URL,当 action 为空时,表示请求的是当前的 URL 页面,里面也可以输入用户想请求的 URL。method 属性指定了表单提交给服务器的 HTTP 请求方法,因为里面要传送数据,所以请求方法为 post。

上面已经完成了前台的 form 表的显示(渲染)工作,这时就需要在视图函数中将代表表单的类传递到前端模板文件中,这里是 form.html 文件。下面在 routes.py 中编写视图函数:

# routes.py 文件中的内容
# 从 flask 库中导入 render_template
from flask import render_template
# 表单内容
# 从 flask_wtf 库中导入 FlaskForm 类
from flask_wtf import FlaskForm

# 从 wtforms 中导入表单的字段对象类      
from wtforms import StringField, PasswordField, BooleanField, SubmitField 

# 从 wtforms.validators 字段验证器中导入 DataRequired 验证函数 
from wtforms.validators import DataRequired    

# 编写 LoginForm 类函数,继承 FlaskForm 基类
class LoginForm(FlaskForm):
     # 表单包括用户名,密码和是否记住密码及一个提交按钮
     username = StringField('Username', validators=[DataRequired()])
     password = PasswordField('Password', validators=[DataRequired()])
     remember = BooleanField('Remember Me')
     submit = SubmitField('Sign In')

# 添加视图函数渲染表单内容
@app.route('/login')
def login():
     # 对类 LoginForm 的实例
     form = LoginForm()
     # 将视图函数中的变量 form 传到模板 form.html 文件中去
     return render_template('form.html', title='第三天', html_form=form)

现在,所有代码已编写完成,通过 pythonn run.py(run.py 文件的内容与第二天教程的内容相同))将 web 程序拉起,开始验证结果,在浏览器中输入 http://127.0.0.1:5000/login,效果图如下所示:

在上面的结果中,猫姐只是完成了表单前台的渲染,但是还不能在表单中发送数据,下面猫姐将介绍,后台接收到用户发送 form 表单内容后,如何进行处理。

上面的例子中,在表单中输入用户名和密码后,点击提交按钮,发现浏览器没有任何反应,后台程序并不能处理 post 请求,所以现在需要修改后台程序 routes.py,让其能够处理 post 请求,更改代码如下:

# routes.py 文件中对视图函数进行修改,处理 post 语法

# 添加视图函数渲染表单内容
# 在装饰器函数中添加 http 请求方法
@app.route('/login',methods=['GET','POST'])
def login():
     # LoginForm 的实例化
     form = LoginForm()
     # 添加处理 post 请求的代码
     if from.validate_on_subimit():
          # 调用 flash 函数, 在 flash 消息中提示用户及记住 remember 状态成功
          flash('登录用户 {}, 记住 remember 状态 {} 成功'.format(form.username.data,form.remember.data), 'success')

     # 将视图函数中的变量 form 传到模板 form.html 文件中去
     return render_template('form.html', title='第三天', html_form=form)

# 解释:flash 消息中,form.username.data 是获取表单中的用户名,form.remember.data 是获取表单中的记住密码的状态(它的状态是 true 或是 false)

当在浏览器中点击提交按钮后,浏览器会发送 post 请求,就会执行上面的 if 条件语句,并渲染 flash 消息到浏览器中。浏览器默认发送的是 get 请求,所以当没有点击提交按钮时,就会跳过 if 语句,直接执行 return 语句。

但是当你调用 flash 函数后,在浏览器中并没有渲染 flash 消息,这是因为需要将消息渲染到基模板中,才能在所有的子模板中显示出来。现在来更新 layout.html 代码:

<!-- layout.html 文件中的内容 -->
<html>
    <head>
      {% if title %}
        <title>{{title}} - 表单的使用 - 喵星在线 </title>
      {% else %}
        <title> 表单的使用 - 喵星在线 </title>
      {% endif %}
    </head>
    <body>
        <!-- 渲染 flash 消息 -->
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{category}}">
                        {{message}}
                    </div>
                {% endfor %}
            {% endif %}
        {% endwith %}
       {% block content %}
       {% endblock %}
    </body>
</html>

在浏览器中打开网址 http://localhost:5000/,查看结果:

在上图中就可以将 flash 的消息渲染出来了,但是,这个 flash 消息并不会消失,并且显示效果也不美观。这是因为我们没有给它添加任何 css 样式,这个问题在后面的教程中也会讲到。

4. 总结

学习完今天的教程,我们掌握了如下技能:

  1. 学习了什么是表单插件
  2. 学习了如何通过表单插件来创建表单对象
  3. 学习了如何在前端模板中渲染表单对象
  4. 学习了在视图函数中处理用户通过表单发送的 post 请求

第四天的内容,我们将会带领大家一起了解使用 flask 的插件来创建数据库,怎么对数据库进行操作,第三天的内容就到这里,喜欢的同学们可以在下面点赞留言,或是访问我的博客地址:http://www.catonlinepy.tech/ 加入我们的 QQ 群进一步交流学习!

5. 代码的获取

今天的课程就到这里,大家可以到 github 上去获取以上教程中的所有代码:https://github.com/miaojie19/…

具体下载代码的命令如下:

# 使用 git 命令下载 flask-course-primary 仓库所有的代码
git clone https://github.com/miaojie19/flask-course-primary.git

# 下载完成后,进入 day3 目录下面,即可看到今天的代码
cd flask-course-primary
cd day3

退出移动版