加载中

This is the third article in the series in which I document my experience writing web applications in Python using the Flask microframework.

The goal of the tutorial series is to develop a decently featured microblogging application that demonstrating total lack of originality I have decided to callmicroblog.

Here is an index of all the articles in the series that have been published to date:

这是我撰写的关于利用基于python的flask框架开发web应用系列文章的第3部分。

该系列文章的目的是建立一个功能完善的微博应用,我决定称它为microblog,虽然这个名字没什么创意。(引用第2部分翻译)

一下是该系列所有已发布文章的索引:


Recap

In the previous chapter of the series we defined a simple template for the home page and used fake objects as placeholders for things we don't have yet, like users or blog posts.

In this article we are going to fill one of those many holes we still have in our app, we will be looking at how to work with web forms.

Web forms are one of the most basic building blocks in any web application. We will be using forms to allow users to write blog posts, and also for logging in to the application.

To follow this chapter along you need to have themicroblogapp as we left it at the end of the previous chapter. Please make sure the app is installed and running.

概要

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

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

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

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

Configuration

To handle our web forms we are going to use the Flask-WTF extension, which in turn wraps the WTForms project in a way that integrates nicely with Flask apps.

Many Flask extensions require some amount configuration, so we are going to setup a configuration file inside our rootmicroblogfolder so that it is easily accessible if it needs to be edited. Here is what we will start with (fileconfig.py):

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

Pretty simple, it's just two settings that our Flask-WTF extension needs. TheCSRF_ENABLEDsetting activates the cross-site request forgery prevention. In most cases you want to have this option enabled as it makes your app more secure.

配置

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

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

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

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

TheSECRET_KEYsetting is only needed with CSRF is enabled, and is used to create a cryptographic token that is used to validate a form. When you write your own apps make sure to set the secrete key to something that is difficult to guess.

Now that we have our config file we need to tell Flask to read it and use it. We can do this right after the Flask app object is created, as follows (fileapp/__init__.py):

from flask import Flask

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

from app import views

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

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

from flask import Flask

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

from app import views

The user login form

Web forms are represented in Flask-WTF as objects, subclassed from classForm. A form subclass simply defines the fields of the form as class variables.

We will create a login form that users will use to identify with the system. The login mechanism that we will support in our app is not the standard username/password type, we will have our users login using their OpenID. OpenIDs have the benefit that the authentication is done by the provider of the OpenID, so we don't have to validate passwords, which makes our site more secure to our users.

用户登录表单

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

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

The OpenID login only requires one string, the so called OpenID. We will also throw a 'remember me' checkbox in the form, so that users can choose to have a cookie installed in their browsers that remembers their login when they come back.

So let's write our first form (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)

I believe the class is pretty much self-explanatory. We imported theFormclass, and the two field classes that we need,TextFieldandBooleanField.

TheRequiredimport is a validator, a function that can be attached to a field to perform validation on the data submitted by the user. TheRequiredvalidator simply checks that the field is not submitted empty. There are many more validators included with Flask-WTF, we will use some more in the future.

使用 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 中还有很多不同作用的表单验证函数,我们将会在后面使用到它们。

Form templates

We will also need a template that contains the HTML that produces the form. The good news is that theLoginFormclass that we just created knows how to render form fields as HTML, so we just need to concentrate on the layout. Here is our login template (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 %}

Note that in this template we are reusing thebase.htmltemplate through theextendstemplate inheritance statement. We will actually do this with all our templates, to ensure a consistent layout across all pages.

表单模板

现在我们的问题就是需要一个显示这个登录表单的模板。好消息是我们刚刚创建的登录表单类知道如何把字段转换成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 继承模板内容。我们会在后面创建的模板中继续使用这种方式,这样可以使我们所有的页面布局保持一致。

There are a few interesting differences between a regular HTML form and our template. The template expects a form object instantiated from the form class we just defined stored in a template argument namedform. We will take care of sending this template argument to the template next, when we write the view function that renders this template.

Theform.hidden_tag()template argument will get replaced with a hidden field that implements the CSRF prevention that we enabled in the configuration. This field needs to be in all your forms if you have CSRF enabled.

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

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

The actual fields of our form are also rendered by the form object, you just have to refer to a{{form.field_name}}template argument in the place where the field should be inserted. Some fields can take arguments. In our case, we are asking the form to generate ouropenidfield with a width of 80 characters.

Since we have not defined the submit button in the form class we have to define it as a regular field. The submit field does not carry any data so it doesn't need to be defined in the form class.

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

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

Form views

The final step before we can see our form is to code a view function that renders the template.

This is actually quite simple since we just need to pass a form object to the template. Here is our new view function (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)

So basically, we have imported ourLoginFormclass, instantiated an object from it, and sent it down to the template. This is all that is required to get form fields rendered.

Let's ignore for now theflashandredirectimports. We'll use them a bit later.

表单视图

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

这个函数相当的简单无趣,因为我们只需要把表单对象传递给模板就行了。下面就是我们这个视图函数的全部内容 (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, 这个先甭理它们,稍后才用得上。

返回顶部
顶部
返回顶部
顶部