Flask开发Day3-Web表单

前言

Web表单是所有Web应用程序中最基本的组成部分之一。表单实现的功能可以有:用户发表动态、登陆认证等

在Flask框架中,有一个专门的插件来处理Web表单,插件是Flask生态中的很重要的一部分,Flask故意设计为只包含核心功能以保持代码的整洁(包括第二章说的jinja2,它就是一个插件,只不过内置了而已),并暴露接口以对接解决不同问题。

Flask插件都是常规的Python三方包,可以使用pip安装:

1
(venv) $ pip install flask-wtf

配置Flask_WTF表单

SECRET_KEY

对于新引入的插件,我们需要配置它,才能够对接使用——你需要决定传入什么样的配置变量列表到框架中。最基本的解决方案是使用app.config对象,它是一个类似字典的对象,可以将配置以键值的方式存储其中。

由于松耦合思想,我们可以在项目的顶层目录创建一个config.py文件专门来存储这些配置信息。

1
2
3
4
import os

class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'this is my blog'

SECRET_KEY是我添加的配置选项,对大多数Flask应用来说,它都是极其重要的。Flask及其一些扩展使用密钥的值作为加密密钥,用于生成签名或令牌。用于抵御CSRF攻击,并且,这个密钥是必配项。(不然打不开网页,它会提示你相关信息)

密钥被定义成由or运算符连接两个项的表达式。第一个项查找环境变量SECRET_KEY的值,第二个项是一个硬编码的字符串。(由你自己定义,什么都行,但是要保密一些)。这种首先检查环境变量中是否存在这个配置,找不到的情况下就使用硬编码字符串的配置变量。


应用配置

拥有配置文件之后,我们要需要启动它(在__init__.py文件中修改如下):

1
2
3
4
5
6
7
from flask import Flask
from config import Config # new1

app = Flask(__name__)
app.config.from_object(Config) # new2

from app import routes
  1. 导入了config这个配置文件的内容
  2. 使用app.config.from_object( )方法来通知Flask读取并使用配置信息

用户登陆表单

Flask-WTF插件使用Python类来表示Web表单。表单类只需将表单的字段定义为类属性即可。我们将表单类单独存储到名为app/forms.py的模块中。

1
2
3
4
5
6
7
8
9
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
  • Flask-WTF插件本身不提供字段类型,所有表示表单字段的类都存放于wtforms模块中,这里通过几个类就定义了表单类型,作用与我们在HTML中书写标签是类似的。
  • 可选参数validators用于验证输入字段是否符合预期(用于控制表单),DataRequired验证器仅验证字段输入是否为空。(表示“被要求的”,即不能为空)

渲染表单

下一步是将表单添加到HTML模板以便渲染到网页上。还记得我们之前第二章介绍过的做法吗,道理是一样的!

我将把登录模板存储在文件app/templates/login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{% extends "base.html" %}

{% block content %}
<h1>Sign In</h1>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}

我们以前可能没见过这个模板中引用的语法:form.hidden_tag()、form.username.label、form.username( ),接下来我会对其一一介绍。

  • form.hidden_tag()模板参数生成了一个隐藏字段,其中包含一个token。 对于保护表单(通俗的讲,这里的token和我们配置的SECRET_KEY变量就保护了表单免受CSRF攻击)。

  • 这里引用的form,其实就是LoginForm表单类的实例,那么继承于LoginForm的实例,就有其相关的类属性(定义的字段类型)。

  • 那么有了字段类型的属性之后,我们就可以引用它,使其渲染到页面啦。form.username.label的作用就在于此,这就应用了我们在form.py定义的 username 字段。

  • 而form.username(size=32 )的意思是:此模板中的username和password字段将size作为参数,将其作为属性添加到<input> HTML元素中。同时最重要的是:form.field_name()是体现字段特性的一个方法。


表单视图

完成这个表单的最后一步就是编写一个新的视图函数来渲染上面创建的模板。

1
2
3
4
5
6
7
8
9
10
from flask import render_template
from app import app
from app.forms import LoginForm

# ...省略的代码

@app.route('/login')
def login():
form = LoginForm()
return render_template('login.html', title='Sign In', form=form)
  • 注意这里参数传递的使用:form=form,第一个form是模板中引用的变量名称,第二个form是实例化的对象。(python中参数传递都经常使用这种方式)

添加登陆链接

在基础模板templates/base.html的导航栏上添加登录的链接,以便访问:

1
2
3
4
5
<div>
Microblog:
<a href="/index">Home</a>
<a href="/login">Login</a>
</div>

页面效果:



接收表单数据

点击提交按钮,浏览器将显示“Method Not Allowed”错误。要知道,我们只实现了如何显示表单,但没有逻辑处理表单数据,这也是报错的原因所在。

Flask-WTF可以轻松完成这部分工作, 以下是视图函数的更新版本,它接受和验证用户提交的数据:

1
2
3
4
5
6
7
8
9
10
from flask import render_template, flash, redirect

@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
flash('Login requested for user {}, remember_me={}'.format(
form.username.data, form.remember_me.data))
return redirect('/index')
return render_template('login.html', title='Sign In', form=form)
  • 装饰器中传入 methods 参数,它告诉Flask这个视图函数接受GETPOST请求(客户端向服务端发送数据一般都为POST请求,通过传入methods参数,你就能告诉Flask哪些请求方法可以被接受。)

  • form.validate_on_submit()实例方法会执行form校验的工作。当浏览器发起GET请求的时候,它返回False,这样视图函数就会跳过if块中的代码,直接转到视图函数的最后一句来渲染模板。

  • 当用户在浏览器点击提交按钮后,浏览器会发送POST请求。form.validate_on_submit()就会获取到所有的数据,运行字段各自的验证器,全部通过之后就会返回True,这表示数据有效。

  • 如果我们想要将某些内容显示给用户看,就可以使用flash()。当你调用flash()函数后,Flask会存储这个消息(但不会直接出现在页面上,模板需要将消息渲染到基础模板中,才能让所有派生出来的模板都能显示出来。)

  • redirect()这个函数指引浏览器自动重定向到它的参数所关联的URL。当前视图函数使用它将用户重定向到应用的主页。


接下来我们更新一下base.html模板,让其接受我们编写的逻辑代码,内容如下:

  • get_flashed_messages()函数将flash存储的信息获取出来。我们使用for循环将其打印输出。

页面逻辑实现:



写在最后

这样我们就完成了Web表单的基本功能了(填写数据、提交数据)。

让我们回顾一下流程操作:

  1. 引入插件、配置插件
  2. 应用配置
  3. 编写表单字段信息
  4. 渲染表单到页面
  5. 接受表单数据的逻辑