定义和访问数据库

应用使用一个SQLite 数据库来储存用户和博客内容。Python 内置了 SQLite 数据库支持,相应的模块为 sqlite3 。

Sqlite非常方便,因为它不需要设置单独的数据库服务器,并且内置于python中。但是,如果并发请求同时写入数据库,会比较慢一点,因为每个写操作是按顺序进行的。小应用没有问题,但是大应用可能就需要考虑换成别的数据库了。

本教程没有详细介绍SQL。如果您不熟悉SQL,请先阅读 SQLite 文档中的相关内容 。

连接到数据库

在使用sqlite数据库(以及大多数其他python数据库)时,首先要做的是创建到它的连接。任何查询和操作都是使用连接执行的,该连接在工作完成后关闭。

在Web应用程序中,此连接通常与请求关联。它在处理请求时的某个时刻创建,并在发送响应之前关闭。

flaskr/db.py
import sqlite3

import click
from flask import current_app, g


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

g 是一个特殊对象,独立于每一个请求。在处理请求过程中,它可以用于储存可能多个函数都会用到的数据。把连接储存于其中,可以多次使用,而不用在同一个请求中每次调用 get_db 时都创建一个新的连接。

current_app 是另一个特殊对象,该对象指向处理请求的 Flask 应用。这里使用了应用工厂,那么在其余的代码中就不会出现应用对象。当应用创建后,在处理一个请求时, get_db 会被调用。这样就需要使用 current_app 。

sqlite3.connect() 建立一个数据库连接,该连接指向配置中的 DATABASE 指定的文件。这个文件现在还没有建立,后面会在初始化数据库的时候建立该文件。

sqlite3.Row 告诉连接返回类似于字典的行,这样可以通过列名称来操作数据。

close_db 通过检查 g.db 来确定连接是否已经建立。如果连接已建立,那么就关闭连接。以后会在应用工厂中告诉应用 close_db 函数,这样每次请求后就会调用它。

创建表

在 SQLite 中,数据储存在表和列中。在储存和调取数据之前需要先创建它们。Flaskr 会把用户数据储存在 user 表中,把博客内容储存在 post 表中。下面创建一个文件储存用于创建空表的 SQL 命令:

flaskr/schema.sql
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL
);

CREATE TABLE post (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  author_id INTEGER NOT NULL,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  FOREIGN KEY (author_id) REFERENCES user (id)
);

将运行这些SQL命令的python函数添加到 db.py 文件:

flaskr/db.py
def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('init-db')
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

open_resource() 打开一个文件,该文件名是相对于 flaskr 包的。这样就不需要考虑以后应用具体部署在哪个位置。get_db 返回一个数据库连接,用于执行文件中的命令。

click.command() 定义名为 init-db 那叫 init_db 并向用户显示成功消息。你可以阅读 命令行界面 了解有关编写命令的更多信息。

在应用程序中注册

这个 close_dbinit_db_command 函数需要在应用程序实例中注册;否则,应用程序将无法使用。然而,既然我们使用了工厂函数,那么在写函数的时候应用实例还无法使用。代替地, 我们写一个函数,把应用作为参数,在函数中进行注册。

flaskr/db.py
def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

app.teardown_appcontext() 通知flask在返回响应后清理时调用该函数。

app.cli.add_command() 添加一个新的可以与 flask 一起工作的命令。

从工厂导入并调用此函数。在返回应用程序之前,将新代码放在工厂功能的尾部。

flaskr/__init__.py
def create_app():
    app = ...
    # existing code omitted

    from . import db
    db.init_app(app)

    return app

初始化数据库文件

既然 init-db 已在应用程序中注册,可以与 flask 命令一起使用了。 使用的方式与前一页的 run 命令类似。

备注

如果您仍在从上一页运行服务器,则可以停止服务器,或者在新终端中运行此命令。如果您使用新的终端,请记住切换到项目目录并激活env,如中所述 安装

运行 init-db 命令:

$ flask --app flaskr init-db
Initialized the database.

现在会有一个 flaskr.sqlite 文件出现在项目所在文件夹的 instance 文件夹 中。

下面请阅读 蓝图和视图 。