在 Flask 中,在請(qǐng)求開始的時(shí)候用 before_request() 裝飾器實(shí)現(xiàn) 打開數(shù)據(jù)庫連接的代碼,然后在請(qǐng)求結(jié)束的時(shí)候用 before_request() 裝飾器關(guān)閉數(shù)據(jù)庫連接。在這個(gè)過程中需要配合 g 對(duì)象。

于是,在 Flask 里一個(gè)使用 SQLite 3 的簡(jiǎn)單例子就是下面這樣:

import sqlite3
from flask import g

DATABASE = '/path/to/database.db'

def connect_db():
    return sqlite3.connect(DATABASE)

@app.before_request
def before_request():
    g.db = connect_db()

@app.teardown_request
def teardown_request(exception):
    if hasattr(g, 'db'):
        g.db.close()

注解

請(qǐng)記住,teardown request 在請(qǐng)求結(jié)束時(shí)總會(huì)運(yùn)行,即使 before-request 處理器 運(yùn)行失敗或者從未運(yùn)行過。我們需要確保數(shù)據(jù)庫連接在關(guān)閉的時(shí)候在那里。

按需連接

上述方法的缺陷在于,它只能用于 Flask 會(huì)執(zhí)行 before-request 處理器的場(chǎng)合下 有效,如果您想要在一個(gè)腳本或者 Python 的交互式終端中訪問數(shù)據(jù)庫。那么您必須 做一些類似下面的代碼的事情:

with app.test_request_context():
    app.preprocess_request()
    # now you can use the g.db object

為了激發(fā)連接代碼的執(zhí)行,使用這種方式的話,您將不能離開對(duì)請(qǐng)求上下文的依賴。 但是您使用以下方法可以使應(yīng)用程序在必要時(shí)才連接:

def get_connection():
    db = getattr(g, '_db', None)
    if db is None:
        db = g._db = connect_db()
    return db

缺點(diǎn)就是,您必須使用 db = get_connection() 而不是僅僅直接使用 g.db 來訪問數(shù)據(jù)庫連接。

簡(jiǎn)化查詢

現(xiàn)在在每個(gè)請(qǐng)求處理函數(shù)里,您都可以訪問 g.db 來獲得當(dāng)前打開的數(shù)據(jù)庫連接。 此時(shí),用一個(gè)輔助函數(shù)簡(jiǎn)化 SQLite 的使用是相當(dāng)有用的:

def query_db(query, args=(), one=False):
    cur = g.db.execute(query, args)
    rv = [dict((cur.description[idx][0], value)
               for idx, value in enumerate(row)) for row in cur.fetchall()]
    return (rv[0] if rv else None) if one else rv

相比起直接使用原始的數(shù)據(jù)指針和連接對(duì)象。這個(gè)隨手即得的小函數(shù)讓操作數(shù)據(jù)庫的操作更為輕松。 像下面這樣使用它:

for user in query_db('select * from users'):
    print user['username'], 'has the id', user['user_id']

如果您只希望得到一個(gè)單獨(dú)的結(jié)果:

user = query_db('select * from users where username = ?',
                [the_username], one=True)
if user is None:
    print 'No such user'
else:
    print the_username, 'has the id', user['user_id']

將變量傳入 SQL 語句時(shí),使用在語句之前使用一個(gè)問號(hào),然后將參數(shù)以鏈表的形式穿進(jìn)去。 永遠(yuǎn)不要直接將他們添加到 SQL 語句中以字符串形式傳入,這樣做將會(huì)允許惡意用戶 以 SQL 注入 的方式攻擊您的應(yīng)用。

初始化數(shù)據(jù)庫模型

關(guān)系數(shù)據(jù)庫需要一個(gè)模型來定義儲(chǔ)存數(shù)據(jù)的模式,所以應(yīng)用程序通常攜帶一個(gè) schema.sql 文件用于創(chuàng)建數(shù)據(jù)庫。提供一個(gè)特定的函數(shù)來創(chuàng)建數(shù)據(jù)庫是個(gè) 不錯(cuò)的主意,以下的函數(shù)就能為您做到這件事:

from contextlib import closing

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql') as f:
            db.cursor().executescript(f.read())
        db.commit()

然后您就可以在 Python 的交互式終端中創(chuàng)建一個(gè)這樣的數(shù)據(jù)庫:

>>> from yourapplication import init_db
>>> init_db()