$ wget http://labfile.oss.aliyuncs.com/courses/29/flaskr.zip //源码
=======================================================
这里我们将称我们的博客应用为flaskr
,也可以取一个不那么 web 2.0 的名字。基本上我们想要它做如下的事情:
- 根据配置文件中的认证允许用户登录以及注销。仅仅支持一个用户。
- 当用户登录后,他们可以添加新的条目,这些条目是由纯文本的标题和 HTML 的正文构成。因为我们信任 用户这里的 HTML 是安全的。
- 页面倒序显示所有条目(新的条目在前),并且用户登入后可以在此添加新条目。
我们将在这个应用中直接使用 SQLite 3 因为它足够应付这种规模的应用。对更大的应用使用 SQLAlchemy 是十分有意义的,它以一种更智能方式处理数据库连接,允许你一次连接多个不同的关系数据库。 你也可以考虑流行的 NoSQL 数据库,前提是你的数据更适合它们。
这是最终应用的一个截图:
====================================
在我们开始之前,让我们为这个应用创建需要的文件夹:
/flaskr
/static
/templates
使用如何命令创建以上文件夹:
cd /home/shiyanlou/Code/shiyanlou_cs29
mkdir flaskr
cd flaskr
mkdir static
mkdir templates
flaskr 文件夹不是一个 Python 的包,只是我们放置文件的地方。 在接下来的步骤中我们会直接把数据库模式和主模块放在这个文件夹中。应用的用户可以通过 HTTP 访问static
文件夹中的文件。这里也是css
和javascript
文件的存放位置。Flask 将会在templates
文件夹中寻找Jinja2模版。在本教程后面创建的模板将会在这个文件夹中。
========================================================================
首先我们要创建数据库模式。对于这个应用仅一张表就足够了,而且我们只想支持 SQLite ,所以很简单。 只要把下面的内容放入一个名为schema.sql
的文件,文件置于刚才创建的 flaskr 文件夹中:
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
这个模式由一个称为entries
的单表构成,在这个表中每行包含一个id
,一个title
和一个text
。id
是一个自增的整数而且是主键,其余两个为非空的字符串。
=======================================================================
现在我们已经有了数据库模式了,我们可以创建应用的模块了。让我们新建文件名为flaskr.py
,并放置于flaskr
文件夹中。对于小应用,直接把配置放在主模块里,正如我们现在要做的一样,是可行的。然而一个更干净的解决方案就是单独创建.ini
或者.py
文件接着加载或者导入里面的值。
在 flaskr.py 中:
# 导入所有的模块
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
# 配置文件
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
下一步我们能够创建真正的应用,接着用同一文件(在flaskr.py
中)中的配置初始化:
# 创建应用
app = Flask(__name__)
app.config.from_object(__name__)
from_object()
将会寻找给定的对象(如果它是一个字符串,则会导入它), 搜寻里面定义的全部大写的变量。在我们的这种情况中,配置文件就是我们上面写的几行代码。 你也可以将他们分别存储到多个文件。
通常,从配置文件中加载配置是一个好的主意。这是可以用from_envvar()
所实现的, 可以用它替换上面的from_object()
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
这种方法我们可以设置一个名为FLASKR_SETTINGS
的环境变量来设定一个配置文件载入后是否覆盖默认值。 静默开关silent=True
告诉 Flask 不去关心这个环境变量键值是否存在。
secret_key
是需要为了保持客户端的会话安全。明智地选择该键,使得它难以猜测,最好是尽可能复杂。
调试标志启用或禁用交互式调试。决不让调试模式在生产系统中启动,因为它将允许用户在服务器上执行代码!
我们还添加了一个轻松地连接到指定数据库的方法,这个方法用于在请求时打开一个连接,并且在交互式 Python shell 和脚本中也能使用。这对以后很方便。
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
最后如果我们想要把那个文件当做独立应用来运行,我们只需在服务器启动文件的末尾添加这一行:
if __name__ == '__main__':
app.run()
如此我们便可以顺利开始运行这个应用,通过使用如下命令:
python3 flaskr.py
你将会看到一个信息,信息提示你服务器启动的地址,这个地址你能够访问到的。
当你在浏览器中访问服务器获得一个 404 页面无法找到的错误时,是因为我们还没有任何视图。我们之后再来关注这些。首先我们应该让数据库工作起来。
====================================
如前面所述,Flaskr 是一个数据库驱动的应用程序,准确地来说,Flaskr 是一个使用关系数据库系统的应用程序。 这样的系统需要一个模式告诉它们如何存储信息。因此在首次启动服务器之前,创建数据库模式是很重要的。
可以通过管道把 schema.sql 作为 sqlite 3 命令的输入来创建这个模式,命令如下:
sqlite3 /tmp/flaskr.db < schema.sql
这种方法的缺点是需要安装 sqlite 3 命令,而并不是每个系统都有安装(实验楼的系统就没有安装)。而且你必须提供数据库的路径,否则将报错。添加一个函数来对初始化数据库是个不错的想法。
如果你想要这么做,首先你必须从contextlib
包中导入contextlib.closing()
函数。修改flaskr
文件如下:
from contextlib import closing
接着我们可以创建一个称为 init_db 函数,该函数用来初始化数据库。为此我们可以使用之前定义的 connect_db 函数。 只要在 connect_db 函数下添加这样的函数:
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
db.cursor().executescript(f.read().decode())
db.commit()
closing()
函数允许我们在with
块中保持数据库连接可用。应用对象的open_resource()
方法在其方框外也支持这个功能,因此可以在with
块中直接使用。这个函数从当前位置(你的flaskr 文件夹)中打开一个文件,并且允许你读取它。我们在这里用它在数据库连接上执行一个脚本。
当我们连接到数据库时会得到一个数据库连接对象(这里命名它为 db
),这个对象提供给我们一个数据库指针。指针上有一个可以执行完整脚本的方法。最后我们不显式地提交更改, SQLite 3 或者其它事务数据库不会这么做。
现在可以在 Python shell 里创建数据库,导入并调用刚才的函数:
>>> from flaskr import init_db
>>> init_db()
注意:
如果你后面得到一个表不能找到的异常,请检查你是否调用了 init_db 函数以及你的表名是否正确 (例如: singular vs. plural)。
----------------------
现在我们知道了怎样建立数据库连接以及在脚本中使用这些连接,但是我们如何能优雅地在请求中这么做?
所有我们的函数中需要数据库连接,因此在请求之前初始化它们,在请求结束后自动关闭他们就很有意义。
Flask 允许我们使用before_request()
,after_request()
和 teardown_request()
装饰器来实现这个功能:
@app.before_request
def before_request():
g.db = connect_db()
@app.teardown_request
def teardown_request(exception):
g.db.close()
使用before_request()
装饰器的函数会在请求之前被调用而且不带参数。使用after_request()
装饰器的函数会在请求之后被调用且传入将要发给客户端的响应。
它们必须返回那个响应对象或是不同的响应对象。但当异常抛出时,它们不一定会被执行,这时可以使用teardown_request()
装饰器。它装饰的函数将在响应构造后执行,并不允许修改请求,返回的值会被忽略。如果在请求已经被处理的时候抛出异常,它会被传递到每个函数,否则会传入一个None
。
我们把当前的数据库连接保存在 Flask 提供的 g 特殊对象中。这个对象只能保存一次请求的信息, 并且在每个函数里都可用。不要用其它对象来保存信息,因为在多线程环境下将不可行。特殊的对象 g 在后台有一些神奇的机制来保证它在做正确的事情。