Python的Flask框架中web表单的教程

 概要

在前面章节我们为主页定义了一个简单的模板,部分尚未实现的模块如用户或帖子等使用模拟的对象作为临时占位。

本章我们将看到如何利用web表单填补这些空白。

web表单是web应用中最基本的构建要素,我们将通过表单来实现用户发帖和应用登录功能。

完成本章内容你需要基于前面章节完成的微博应用代码,请确认这些代码已安装并能正常运行。

配置

Flask-WTF是WTForms项目的Flask框架扩展,我们将用他来帮助我们处理web表单。

大部分Flask扩展都需要定义相关配置项,所以我们先来在应用根目录下创建一个配置文件以备使用。我们先这样创建 (fileconfig.py):

CSRF_ENABLED = True
SECRET_KEY = 'you-will-never-guess'

很简单吧,这是Flask-WTF需要用到的2个配置项。CSRF_ENABLED配置启用了跨站请求攻击保护,大部分情况下你都需要开启此功能,这能使你的应用更安全。

SECRET_KEY设置当CSRF启用时有效,这将生成一个加密的token供表单验证使用,你要确保这个KEY足够复杂不会被简单推测。

现在这个配置文件已经基本可用了。项目创建完成我们可以创建如下文件并编辑(fileapp/__init__.py):

from flask import Flask

app = Flask(__name__)
app.config.from_object('config')

from app import views

用户登录表单

使用Flask-WTF创建的表单就像一个对象,需要从Form类继承子类。然后在这个子类中定义一些类的属性变量作为表单字段就可以了。

我们要创建一个登录表单,用来进行用户身份识别。但跟平常需要验证用户名和密码的登录方式不同,我们将使用 OpenId 来处理登录过程。使用OpenId的好处就是我们不用管那些用户名和密码的认证过程,交给 OpenId 去搞定,它会返回给我们用户验证后的数据。这样对于使用我们网站的用户而言也更安全。

使用 OpenId 登录只需要一个字符串,然后发送给 OpenId 服务器就行了。另外我们还需要在表单中加一个“记住我” 的选项框,这个是送给那些不想每次来我们网站都要进行身份认证的人。选择这个选项后,首次登录时会用cookie在他们的浏览器上记住他们的登录信息,下次再进入网站时就不需要进行登录操作。

开始我们的第一个表单吧 (fileapp/forms.py):

from flask.ext.wtf import Form, TextField, BooleanField
from flask.ext.wtf import Required

class LoginForm(Form):
  openid = TextField('openid', validators = [Required()])
  remember_me = BooleanField('remember_me', default = False)

欣赏一下这个类,多么的简洁,多么的一目了然。如此简单,但又十分的富有内涵。我们引入了一个 Form 类,然后继承这个类,按需求还添加了 TextField 和 BooleanField 这两个字段。

另外还引入了一个表单验证函数 Required,这种验证函数可以附加在字段里面,在用户提交表单时它们会用来检查用户填写的数据。这个 Required 函数是用来防止用户提交空数据。Flask-WTF 中还有很多不同作用的表单验证函数,我们将会在后面使用到它们。


表单模板

现在我们的问题就是需要一个显示这个登录表单的模板。好消息是我们刚刚创建的登录表单类知道如何把字段转换成HTML,所以我们只需要把注意力集中到页面布局上。下面就是我们的登录表单的模板 (fileapp/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 %}

容我啰嗦一下,在这个模板中,我们又一次使用了模板继承的方式。使用 extends 语句从 base.html 继承模板内容。我们会在后面创建的模板中继续使用这种方式,这样可以使我们所有的页面布局保持一致。

这个登录模板跟普通的HTML表单有些明显的区别,它使用模板参数 {{ ... }} 来实例化表单字段,而表单字段又来源于我们刚刚定义的表单类,模板参数中使用了 form 这个名称。当我们使用视图函数引用表单类并渲染到模板时,我们要特别注意这个把表单类传递到模板的变量名。

我们在配置中开启了CSRF(跨站伪造请求)功能,模板参数 {{ form.hidden_tag() }} 会被替换成一个具有防止CSRF功能的隐藏表单字段。在开启了CSRF功能后,所有模板的表单中都需要添加这个模板参数。

我们定义的表单对象中的字段同样也能被模板渲染,只需要在模板合适的位置添加类似于 {{ form.field_name }} 这样的模板参数,相关字段就会在被定义的位置出现。另外还有一些字段是可以传参数,比如这个 openid 字段,我们就添加了一个参数让它显示的宽度增加到80个字符。

由于我们没有在表单中定义一个提交功能的按钮,所以在这里只能以普通表单字段的方式来做了。不过说起来区区一个按钮,在表单中跟任何数据都没有关系,的确也没有在表单类中定义的必要。

表单视图

见证奇迹的时刻最后一步,我们马上要来写一个渲染登录表单对象到模板的视图函数。

这个函数相当的简单无趣,因为我们只需要把表单对象传递给模板就行了。下面就是我们这个视图函数的全部内容 (fileapp/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)

我们引入登录表单类,然后把它实例化到一个变量,最后再把这个变量传给模板。要渲染表单字段必须的事情也就这些。

上面的代码中还引入了两个新对象: falsh 和 redirect, 这个先甭理它们,稍后才用得上。

另外还做了一件事就是在路由装饰器中添加一个新方法。让 Flask 明白我们这个视图函数支持 GET 和 POST 请求。否则这个视图函数只会响应 GET 请求。我们需要得到用户填写表单后提交的数据,这些数据是从 POST 请求中传递过来的。

你可以通过在浏览器中测试这个程序来了解上面所说的。 按照视图函数关联的路由,你应该在浏览器中输入 http://localhost:5000/login

由于我们还没有写任何接收数据的代码,所以现在你在页面中点提交按钮还没有任何效果。

从表单中接收数据

另外值得一提的是, Flask-WTF 对表单提交数据的处理使我们的接下来要做的事情变得简单了。下面就是我们这个登录视图函数的新版本, 加入了表单数据验证和处理 (fileapp/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 函数非常有用,比如为用户的一些操作提供消息反馈。

flash 函数提供的消息不会自动出现在我们的网站页面中,所以我们需要做点事情让它在页面中显示出来。为了让我们所有页面都能有这项激动人心的功能,所以就把它添加到基础模板中吧, 下面是更新后的基础模板 (fileapp/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>

模板中显示 flash 消息的功能希望你能明白。

在视图函数中我们使用的另一个新函数就是 redirect. 这个函数会通知用户的浏览器跳转到指定的地址。在我们的视图函数中,我们使用它跳转到了首页。注意跳转结束后页面上还会显示 flash 函数传递的消息哦。

激动人心的时刻到了,运行我们的程序吧,看看表单是如何工作的吧。不要填写表单中的 openid 字段,看看 Required 这个验证函数是如何发挥威力,把一切发起空数据的请求阻止在千里之外。

改善一下字段验证

我们程序目前状况不错,提交不合要求的数据会被阻止,还会返回表单让用户修改,基本满足我们要求。

但似乎还少点什么。如果我们在用户提交数据失败后给用户点提示,让他们知道什么原因引起的,岂不妙哉!太幸运了,用 Flask-WTF 可以轻松解决这个问题。

当表单字段验证失败时, Flask-WTF 会添加一个错误消息到表单对象。这些消息在模板中也是可以使用的,所以我们只需要在模板中添加一点点东西就OK了。

这个就是我们添加了验证消息的登录模板 (fileapp/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.errors.openid %}
    <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 字段的右边添加了一个循环语句,它会把openid字段验证失败的消息都显示出来。不论你的表单有多少字段,所有表单字段验证失败的错误消息都可以用 form.errors.字段名 这种方式来使用。这个表单中我们的是 form.errors.openid。为了让错误消息引起用户的注意,我们还给消息添加了显示红色的 css 样式。
 
处理 OpenID 登录

现实生活中,我们发现有很多人都不知道他们拥有一些公共账号。一部分大牌的网站或服务商都会为他们的会员提供公共账号的认证。举个栗子,如果你有一个 google 账号,其实你就有了一个公共账号,类似的还有 Yahoo, AOL, Flickr 等。

为了方便我们的用户能简单的使用他们的公共账号,我们将把这些公共账号的链接添加到一个列表,这样用户就不用自手工输入了。

我们要把一些提供给用户的公共账号服务商定义到一个列表里面,这个列表就放到配置文件中吧 (fileconfig.py):

CSRF_ENABLED = True
SECRET_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'])

我们从 app.config 中引入了公共账号服务商的配置列表,然后把它作为一个参数通过 render_template 函数引入到模板。

接下来要做的我想你也猜得到,我们需要在登录模板中把这些服务商链接显示出来。

<!-- 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.errors.openid %}
    <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 %}

这次的模板添加的东西似乎有点多。一些公共账号需要提供用户名,为了解决这个我们用了点 javascript。当用户点击相关的公共账号链接时,需要用户名的公共账号会提示用户输入用户名, javascript 会把用户名处理成可用的公共账号,最后再插入到 openid 字段的文本框中。

下面这个是在登录页面点击 google 链接后显示的截图:

 结束语

我们目前在用户登录表单中取得了一些进展,但用户登入这个网站还是啥事儿都不能干。目前我们还一直在登录的界面上小打小闹,是因为我们还没有记录一切的数据库。

所以从下节开始,我们将会整一个数据库起来,然后真正完成我们的登录系统。不要走开,请继续关注。

微博(microblog) 当前版本源码从下面地址下载:

下载地址:microblog-0.3.zip

(0)

相关推荐

  • 一个基于flask的web应用诞生 用户注册功能开发(5)

    下面把角色分为两种,普通用户和管理员用户,至少对于普通用户来说,直接修改DB是不可取的,要有用户注册的功能,下面就开始进行用户注册的开发. 用户表 首先要想好用户注册的时候需要提供什么信息:用户名.密码.昵称.邮箱.生日.性别.自我介绍,下面就按照这些信息修改用户模型: class User(db.Model): __tablename__="users" id=db.Column(db.Integer,primary_key=True) username=db.Column(db.S

  • 在Python的Flask框架中验证注册用户的Email的方法

    本教程详细介绍在用户注册过程中如何去验证他们的email地址. 工作流程上来讲,在用户注册一个新账户后会寄送一个确认信.直到用户按指示完成了邮件中的"验证",否则他们的账户会一直处于"未验证"状态.这是大多数网络应用会采用的工作流程. 这当中很重要的一件事就是,未验证的用户有什么权限?或者说,对于你的应用,他们是有全部权限呢,还是被限制的权限呢,还是根本没有权限?对于本教程中的应用,未验证用户会在登录后进到一个页面,会提醒他们只有验证了账户才可以进入应用. 开始前说

  • Python的Django框架中forms表单类的使用方法详解

    Form表单的功能 自动生成HTML表单元素 检查表单数据的合法性 如果验证错误,重新显示表单(数据不会重置) 数据类型转换(字符类型的数据转换成相应的Python类型) Form相关的对象包括 Widget:用来渲染成HTML元素的工具,如:forms.Textarea对应HTML中的<textarea>标签 Field:Form对象中的一个字段,如:EmailField表示email字段,如果这个字段不是有效的email格式,就会产生错误. Form:一系列Field对象的集合,负责验证和

  • 使用Python的Flask框架表单插件Flask-WTF实现Web登录验证

    表单是让用户与我们的网页应用程序交互的基本元素.Flask 本身并不会帮助我们处理表单,但是 Flask-WTF 扩展让我们在我们的 Flask 应用程序中使用流行的 WTForms 包.这个包使得定义表单和处理提交容易一些. Flask-WTF 我们想要使用 Flask-WTF 做的第一件事情(在安装它以后,GitHub项目页:https://github.com/lepture/flask-wtf )就是在 myapp.forms 包中定义一个表单. # ourapp/forms.py fr

  • 用Python的urllib库提交WEB表单

    复制代码 代码如下: class EntryDemo( Frame ): """Demonstrate Entrys and Event binding""" chosenrange = 2 url_login="http://.../ipgw/ipgw.ipgw/" uid = '' #用户名 password = '' # 密码 operation = '' # 操作 range = '2' # 范围 the_page =

  • 用Python实现web端用户登录和注册功能的教程

    用户管理是绝大部分Web网站都需要解决的问题.用户管理涉及到用户注册和登录. 用户注册相对简单,我们可以先通过API把用户注册这个功能实现了: _RE_MD5 = re.compile(r'^[0-9a-f]{32}$') @api @post('/api/users') def register_user(): i = ctx.request.input(name='', email='', password='') name = i.name.strip() email = i.email.

  • Python实现模拟登录及表单提交的方法

    本文实例讲述了Python实现模拟登录及表单提交的方法.分享给大家供大家参考.具体实现方法如下: # -*- coding: utf-8 -*- import re import urllib import urllib2 import cookielib #获取CSDN博客标题和正文 url = "http://blog.csdn.net/[username]/archive/2010/07/05/5712850.aspx" sock = urllib.urlopen(url) ht

  • python Django框架实现自定义表单提交

    除了使用Django内置表单,有时往往我们需要自定义表单.对于自定义表单Post方式提交往往会带来由CSRF(跨站请求伪造)产生的错误"CSRF verification failed. Request aborted." 本篇文章主要针对"表单提交"和"Ajax提交"两种方式来解决CSRF带来的错误 一.表单提交 Template: <!DOCTYPE html> <html lang="en"> &

  • Python的Django框架中的表单处理示例

    组建一个关于书籍.作者.出版社的例子: from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) c

  • 在Python的Flask中使用WTForms表单框架的基础教程

    下载和安装 安装 WTForms 最简单的方式是使用 easy_install 和 pip: easy_install WTForms # or pip install WTForms 你可以从 PyPI 手动 下载 WTForms 然后运行 python setup.py install . 如果你是那种喜欢这一切风险的人, 就运行来自 Git 的最新版本, 你能够获取最新变更集的 打包版本, 或者前往 项目主页 克隆代码仓库. 主要概念 Forms 类是 WTForms 的核心容器. 表单(

随机推荐