在上一章节中,咱们定义了一个简单的模板,使用占位符来虚拟了暂未实现的部分,好比用户以及文章等。javascript
在本章咱们将要讲述应用程序的特性之一–表单,咱们将会详细讨论如何使用 web 表单。html
Web 表单是在任何一个 web 应用程序中最基本的一部分。咱们将使用表单容许用户写文章,以及登陆到应用程序中。java
咱们接下来说述的正是咱们上一章离开的地方,因此你可能要确保应用程序 microblog 正确地安装和工做。python
为了可以处理 web 表单,咱们将使用 Flask-WTF ,该扩展封装了 WTForms 而且恰当地集成进 Flask 中。git
许多 Flask 扩展须要大量的配置,所以咱们将要在 microblog 文件夹的根目录下建立一个配置文件以致于容易被编辑。这就是咱们将要开始的(文件 config.py):github
CSRF_ENABLED = True SECRET_KEY = 'you-will-never-guess'
十分简单吧,咱们的 Flaks-WTF 扩展只须要两个配置。 CSRF_ENABLED 配置是为了激活 跨站点请求伪造 保护。在大多数状况下,你须要激活该配置使得你的应用程序更安全些。web
SECRET_KEY 配置仅仅当 CSRF 激活的时候才须要,它是用来创建一个加密的令牌,用于验证一个表单。当你编写本身的应用程序的时候,请务必设置很难被猜想到密钥。数据库
既然咱们有了配置文件,咱们须要告诉 Flask 去读取以及使用它。咱们能够在 Flask 应用程序对象被建立后去作,方式以下(文件 app/__init__.py):flask
from flask import Flask app = Flask(__name__) app.config.from_object('config') from app import views
在 Flask-WTF 中,表单是表示成对象,Form 类的子类。一个表单子类简单地把表单的域定义成类的变量。浏览器
咱们将要建立一个登陆表单,用户用于认证系统。在咱们应用程序中支持的登陆机制不是标准的用户名/密码类型,咱们将使用 OpenID。OpenIDs 的好处就是认证是由 OpenID 的提供者完成的,所以咱们不须要验证密码,这会让咱们的网站对用户而言更加安全。
OpenID 登陆仅仅须要一个字符串,被称为 OpenID。咱们将在表单上提供一个 ‘remember me’ 的选择框,以致于用户能够选择在他们的网页浏览器上种植 cookie ,当他们再次访问的时候,浏览器可以记住他们的登陆。
因此让咱们编写第一个表单(文件 app/forms.py):
from flask.ext.wtf import Form from wtforms import StringField, BooleanField from wtforms.validators import DataRequired class LoginForm(Form): openid = StringField('openid', validators=[DataRequired()]) remember_me = BooleanField('remember_me', default=False)
我相信这个类不言而明。咱们导入 Form 类,接着导入两个咱们须要的字段类,TextField 和 BooleanField。
DataRequired 验证器只是简单地检查相应域提交的数据是不是空。在 Flask-WTF 中有许多的验证器,咱们将会在之后看到它们。
咱们一样须要一个包含生成表单的 HTML 的模板。好消息是咱们刚刚建立的 LoginForm 类知道如何呈现为 HTML 表单字段,因此咱们只须要集中精力在布局上。这里就是咱们登陆的模板(文件 app/templates/login.html):
<!-- extend from base layout --> {% extends "base.html" %} {% block content %} <h1>Sign In</h1> <form action="" method="post" name="login"> {{form.hidden_tag()}} <p> Please enter your OpenID:<br> {{form.openid(size=80)}}<br> </p> <p>{{form.remember_me}} Remember Me</p> <p><input type="submit" value="Sign In"></p> </form> {% endblock %}
请注意,此模板中,咱们重用了 base.html 模板经过 extends 模板继承声明语句。实际上,咱们将在全部咱们的模板中作到这一点,以确保全部网页的布局一致性。
在咱们的模板与常规的 HTML 表单之间存在一些有意思的不一样处。模板指望一个实例化自咱们刚才建立地表单类的表单对象储存成一个模板参数,称为 form。当咱们编写渲染这个模板的视图函数的时候,咱们将会特别注意传送这个模板参数到模板中。
form.hidden_tag() 模板参数将被替换为一个隐藏字段,用来是实如今配置中激活的 CSRF 保护。若是你已经激活了 CSRF,这个字段须要出如今你全部的表单中。
咱们表单中实际的字段也将会被表单对象渲染,你只必须在字段应该被插入的地方指明一个 {{form.field_name}} 模板参数。某些字段是能够带参数的。在咱们的例子中,咱们要求表单生成一个 80 个字符宽度的 openid 字段。
由于咱们并无在表单中定义提交按钮,咱们必须按照普通的字段来定义。提交字段实际并不携带数据所以没有必要在表单类中定义。
在咱们看到咱们表单前的最后一步就是编写渲染模板的视图函数的代码。
实际上这是十分简单由于咱们只须要把一个表单对象传入模板中。这就是咱们新的视图函数(文件 app/views.py):
from flask import render_template, flash, redirect from app import app from .forms import LoginForm # index view function suppressed for brevity @app.route('/login', methods = ['GET', 'POST']) def login(): form = LoginForm() return render_template('login.html', title = 'Sign In', form = form)
因此基本上,咱们已经导入 LoginForm 类,从这个类实例化一个对象,接着把它传入到模板中。这就是咱们渲染表单全部要作的。
让咱们先忽略 flash 以及 redirect 的导入。咱们会在后面介绍。
这里惟一的新的知识点就是路由装饰器的 methods 参数。参数告诉 Flask 这个视图函数接受 GET 和 POST 请求。若是不带参数的话,视图只接受 GET 请求。
这个时候你能够尝试运行应用程序,在浏览器上看看表单。在你运行应用程序后,你须要在浏览器上打开 http://localhost:5000/login 。
咱们暂时尚未编写接收数据的代码,所以此时按提交按钮不会有任何做用。
Flask-WTF 使得工做变得简单的另一点就是处理提交的数据。这里是咱们登陆视图函数更新的版本,它验证而且存储表单数据 (文件 app/views.py):
@app.route('/login', methods = ['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): flash('Login requested for OpenID="' + form.openid.data + '", remember_me=' + str(form.remember_me.data)) return redirect('/index') return render_template('login.html', title = 'Sign In', form = form)
validate_on_submit 方法作了全部表单处理工做。当表单正在展现给用户的时候调用它,它会返回 False.
若是 validate_on_submit 在表单提交请求中被调用,它将会收集全部的数据,对字段进行验证,若是全部的事情都经过的话,它将会返回 True,表示数据都是合法的。这就是说明数据是安全的,而且被应用程序给接受了。
若是至少一个字段验证失败的话,它将会返回 False,接着表单会从新呈现给用户,这也将给用户一次机会去修改错误。咱们将会看到当验证失败后如何显示错误信息。
当 validate_on_submit 返回 True,咱们的登陆视图函数调用了两个新的函数,导入自 Flask。flash 函数是一种快速的方式下呈现给用户的页面上显示一个消息。在咱们的例子中,我将会使用它来调试,由于咱们目前还不具有用户登陆的必备的基础设施,相反咱们将会用它来显示提交的数据。flash 函数在生产服务器上也是十分有做用的,用来提供反馈给用户有关的行动。
闪现的消息将不会自动地出如今咱们的页面上,咱们的模板须要加入展现消息的内容。咱们将添加这些消息到咱们的基础模板中,这样全部的模板都能继承这个函数。这是更新后的基础模板(文件 app/templates/base.html):
<html> <head> {% if title %} <title>{{title}} - microblog</title> {% else %} <title>microblog</title> {% endif %} </head> <body> <div>Microblog: <a href="/index">Home</a></div> <hr> {% with messages = get_flashed_messages() %} {% if messages %} <ul> {% for message in messages %} <li>{{ message }} </li> {% endfor %} </ul> {% endif %} {% endwith %} {% block content %}{% endblock %} </body> </html>
显示闪现消息的技术但愿是不言自明的。
在咱们登陆视图这里使用的其它新的函数就是 redirect。这个函数告诉网页浏览器引导到一个不一样的页面而不是请求的页面。在咱们的视图函数中咱们用它重定向到前面已经完成的首页上。要注意地是,闪现消息将会显示即便视图函数是以重定向结束。
是到了启动应用程序的时候,测试下表单是如何工做的。确保您尝试提交表单的时候,OpenID 字段为空,看看 Required 验证器是如何中断提交的过程。
现阶段的应用程序,若是表单提交不合理的数据将不会被接受。相反,会返回表单让用户提交合法的数据。这确实是咱们想要的。
而后,好像咱们缺乏了一个提示用户表单哪里出错了。幸运的是,Flask-WTF 也可以轻易地作到这一点。
当字段验证失败的时候, Flask-WTF 会向表单对象中添加描述性的错误信息。这些信息是能够在模板中使用的,所以咱们只须要增长一些逻辑来获取它。
这就是咱们含有字段验证信息的登陆模板(文件 app/templates/login.html):
<!-- extend base layout --> {% extends "base.html" %} {% block content %} <h1>Sign In</h1> <form action="" method="post" name="login"> {{ form.hidden_tag() }} <p> Please enter your OpenID:<br> {{ form.openid(size=80) }}<br> {% for error in form.openid.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %}<br> </p> <p>{{ form.remember_me }} Remember Me</p> <p><input type="submit" value="Sign In"></p> </form> {% endblock %}
惟一的变化就是咱们增长了一个循环获取验证 openid 字段的信息。一般状况下,任何须要验证的字段都会把错误信息放入 form.field_name.errors 下。在咱们的例子中,咱们使用 form.openid.errors 。咱们以红色的字体颜色显示这些错误信息以引发用户的注意。
事实上,不少用户并不知道他们已经有一些 OpenIDs。一些大的互联网服务提供商支持 OpenID 认证本身的会员这并非众所周知的。好比,若是你有一个 Google 的帐号,你也就有了一个它们的 OpenID。
为了让用户更方便地使用这些经常使用的 OpenID 登陆到咱们的网站,咱们把它们的连接转成短名称,用户没必要手动地输入这些 OpenID。
我首先开始定义一个 OpenID 提供者的列表。咱们能够把它们写入咱们的配置文件中(文件 config ):
CSRF_ENABLED = TrueSECRET_KEY = 'you-will-never-guess'OPENID_PROVIDERS = [ { 'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id' }, { 'name': 'Yahoo', 'url': 'https://me.yahoo.com' }, { 'name': 'AOL', 'url': 'http://openid.aol.com/<username>' }, { 'name': 'Flickr', 'url': 'http://www.flickr.com/<username>' }, { 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }]
如今让咱们看看如何在咱们登陆视图函数中使用它们:
@app.route('/login', methods = ['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): flash('Login requested for OpenID="' + form.openid.data + '", remember_me=' + str(form.remember_me.data)) return redirect('/index') return render_template('login.html', title = 'Sign In', form = form, providers = app.config['OPENID_PROVIDERS'])
咱们从配置中获取 OPENID_PROVIDERS,接着把它做为 render_template 中一个参数传入模板中。
我敢确信大家已经猜到了,咱们还须要多作一步来达到目的。咱们如今就来讲明如何在登陆模板中渲染这些提供商的连接(文件 app/templates/login.html):
<!-- extend base layout --> {% extends "base.html" %} {% block content %} <script type="text/javascript"> function set_openid(openid, pr) { u = openid.search('<username>') if (u != -1) { // openid requires username user = prompt('Enter your ' + pr + ' username:') openid = openid.substr(0, u) + user } form = document.forms['login']; form.elements['openid'].value = openid } </script> <h1>Sign In</h1> <form action="" method="post" name="login"> {{ form.hidden_tag() }} <p> Please enter your OpenID, or select one of the providers below:<br> {{ form.openid(size=80) }} {% for error in form.openid.errors %} <span style="color: red;">[{{error}}]</span> {% endfor %}<br> |{% for pr in providers %} <a href="javascript:set_openid('{{ pr.url }}', '{{ pr.name }}');">{{ pr.name }}</a> | {% endfor %} </p> <p>{{ form.remember_me }} Remember Me</p> <p><input type="submit" value="Sign In"></p> </form> {% endblock %}
模板变得跟刚才不同了。一些 OpenIDs 含有用户名,所以对于这些用户,咱们必须利用 javascript 的魔力提示用户输入用户名而且组成 OpenIDs。当用户点击一个 OpenIDs 提供商的连接而且(可选)输入用户名,该提供商相应的 OpenID 就被写入到文本域中。
下面就是点击 Google OpenID 连接后,咱们登陆界面的一个截图:
尽管咱们在登陆表单上已经取得了不少进展,咱们实际上没有作任何用户登陆到咱们的系统,到目前为止咱们所作的是登陆过程的 GUI 方面。这是由于在作实际登陆以前,咱们须要有一个数据库,那里能够记录咱们的用户。
在下一章中,咱们会获得咱们的数据库而且运行它,接着咱们将完成咱们的登陆系统。敬请关注后续文章。
若是你想要节省时间的话,你能够下载 microblog-0.3.zip。
可是请注意的是 zip 文件已经不包含 flask 虚拟环境了,若是你想要运行应用程序的话,请按照第一章的步骤本身建立它。