首页 > Python基础教程 >
-
带你认识 flask web 表单
为梦想而战,上节课为你讲解了 flask 模板,还不知道的伙伴可以回去回顾一下,链接在此 带你认识 flask 的模板
Flask-WTF简介
我将使用Flask-WTF插件来处理本应用中的Web表单,它对WTForms进行了浅层次的封装以便和Flask完美结合。这是本应用引入的第一个Flask插件,但绝不是最后一个。插件是Flask生态中的举足轻重的一部分,Flask故意设计为只包含核心功能以保持代码的整洁,并暴露接口以对接解决不同问题的插件。
Flask插件都是常规的Python三方包,可以使用pip
安装。那就继续在你的虚拟环境中安装Flask-WTF吧:
由于今天电脑出问题了,重装系统之后,还没安装 anaconda3 今天无法演示了,抱歉了。
(venv) $ pip install flask-wtf
配置
到目前为止,这个应用程序都非常简单,因此我不需要考虑它的配置。但是,除了最简单的应用,你会发现Flask(也可能是Flask插件)为使用者提供了一些可自由配置的选项。你需要决定传入什么样的配置变量列表到框架中。
有几种途径来为应用指定配置选项。最基本的解决方案是使用app.config
对象,它是一个类似字典的对象,可以将配置以键值的方式存储其中。例如,你可以这样做:
app = Flask(__name__)
app.config['SECRET_KEY'] = 'you-will-never-guess'
# ... add more variables here as needed
上面的代码虽然可以为应用创建配置,但是我有松耦合的癖好。因此,我不会让配置和应用代码处于同一个部分,而是使用稍微复杂点的结构,将配置保存到一个单独的文件中。
使用类来存储配置变量,才是我真正的风格。我会将这个配置类存储到单独的Python模块,以保持良好的组织结构。下面就让你见识一下这个存储在顶级目录下,名为config.py的模块的配置类吧:
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
简单的不像话,有没有?配置设置被定义为Config
类中的属性。一旦应用程序需要更多配置选项,直接依样画葫芦,附加到这个类上即可,稍后如果我发现需要多个配置集,则可以创建它的子类。现在则不用操心。
SECRET_KEY
是我添加的唯一配置选项,对大多数Flask应用来说,它都是极其重要的。Flask及其一些扩展使用密钥的值作为加密密钥,用于生成签名或令牌。Flask-WTF插件使用它来保护网页表单免受名为Cross-Site Request Forgery或CSRF(发音为“seasurf”)的恶意攻击。顾名思义,密钥应该是隐密的,因为由它产生的令牌和签名的加密强度保证,取决于除了可信维护者之外,没有任何人能够获得它。
密钥被定义成由or
运算符连接两个项的表达式。第一个项查找环境变量SECRET_KEY
的值,第二个项是一个硬编码的字符串。这种首先检查环境变量中是否存在这个配置,找不到的情况下就使用硬编码字符串的配置变量的模式你将会反复看到。在开发阶段,安全性要求较低,因此可以直接使用硬编码字符串。但是,当应用部署到生产服务器上的时候,我将设置一个独一无二且难以揣摩的环境变量,这样,服务器就拥有了一个别人未知的安全密钥了。
拥有了这样一份配置文件,我还需要通知Flask读取并使用它。可以在生成Flask应用之后,利用app.config.from_object()
方法来完成这个操作:
from flask import Flask
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
from app import routes
导入Config
类的方式,乍一看可能会让人感到困惑,不过如果你注意到从flask
包导入Flask
类的过程,就会发现这其实是类似的操作。显而易见,小写的“config”是Python模块config.py的名字,另一个含有大写“C”的是类。
正如我上面提到的,可以使用app.config
中的字典语法来访问配置项。在下面的Python交互式会话中,你可以看到密钥的值:
from microblog import app
'SECRET_KEY'] app.config[
'you-will-never-guess'
用户登录表单
Flask-WTF插件使用Python类来表示Web表单。表单类只需将表单的字段定义为类属性即可。
为了再次践行我的松耦合原则,我会将表单类单独存储到名为app/forms.py的模块中。就让我们来定义用户登录表单来做一个开始吧,它会要求用户输入username和password,并提供一个“remember me”的复选框和提交按钮:
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插件使用flask_ <name>
命名约定来导入,Flask-WTF的所有内容都在flask_wtf
包中。在本例中,app/forms.py模块的顶部从flask_wtf
导入了名为FlaskForm
的基类。
由于Flask-WTF插件本身不提供字段类型,因此我直接从WTForms包中导入了四个表示表单字段的类。每个字段类都接受一个描述或别名作为第一个参数,并生成一个实例来作为LoginForm
的类属性。
你在一些字段中看到的可选参数validators
用于验证输入字段是否符合预期。DataRequired
验证器仅验证字段输入是否为空。更多的验证器将会在未来的表单中接触到。
表单模板
下一步是将表单添加到HTML模板以便渲染到网页上。令人高兴的是在LoginForm类中定义的字段支持自渲染为HTML元素,所以这个任务相当简单。我将把登录模板存储在文件*app/templates/login.html *中,代码如下:
{% 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 %}
一如第二章,在这个模板中我再次使用了extends
来继承base.html
基础模板。事实上,我将会对所有的模板继承基础模板,以保持顶部导航栏风格统一。
这个模板需要一个form参数的传入到渲染模板的函数中,form来自于LoginForm
类的实例化,不过我现在还没有编写它。
HTML<form>
元素被用作Web表单的容器。表单的action
属性告诉浏览器在提交用户在表单中输入的信息时应该请求的URL。当action
设置为空字符串时,表单将被提交给当前地址栏中的URL,即当前页面。 method
属性指定了将表单提交给服务器时应该使用的HTTP请求方法。默认情况下是用GET
请求发送,但几乎在所有情况下,使用POST
请求会提供更好的用户体验,因为这种类型的请求可以在请求的主体中提交表单数据, GET
请求将表单字段添加到URL,会使浏览器地址栏变得混乱。
form.hidden_tag()
模板参数生成了一个隐藏字段,其中包含一个用于保护表单免受CSRF攻击的token
。对于保护表单,你需要做的所有事情就是在模板中包括这个隐藏的字段,并在Flask配置中定义SECRET_KEY
变量,Flask-WTF会完成剩下的工作。
如果你以前编写过HTML Web表单,那么你会发现一个奇怪的现象——在此模板中没有HTML表单元素,这是因为表单的字段对象的在渲染时会自动转化为HTML元素。我只需在需要字段标签的地方加上{{ form.<field_name>.label }}
,需要这个字段的地方加上{{ form.<field_name>() }}
。对于需要附加HTML属性的字段,可以作为关键字参数传递到函数中。此模板中的username和password字段将size
作为参数,将其作为属性添加到<input>
HTML元素中。你也可以通过这种手段为表单字段设置class和id属性。
表单视图
完成这个表单的最后一步就是编写一个新的视图函数来渲染上面创建的模板。
函数的逻辑只需创建一个form实例,并将其传入渲染模板的函数中即可,然后用*/login* URL来关联它。这个视图函数也存储到app/routes.py模块中,代码如下:
from flask import render_template
from app import app
from app.forms import LoginForm
# ...
def login():
form = LoginForm()
return render_template('login.html', title='Sign In', form=form)
我从forms.py导入LoginForm
类,并生成了一个实例传入模板。form=form
的语法看起来奇怪,这是Python函数或方法传入关键字参数的方式,左边的form
代表在模板中引用的变量名称,右边则是传入的form实例。这就是获取表单字段渲染结果的所有代码了。
在基础模板templates/base.html的导航栏上添加登录的链接,以便访问:
<div>
Microblog:
<a href="/index">Home</a>
<a href="/login">Login</a>
</div>
此时,你可以验证结果了。运行该应用,在浏览器的地址栏中输入http://localhost:5000/
,然后点击顶部导航栏中的“Login”链接来查看新的登录表单。是不是非常炫酷?