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

于是,在 Flask 里一個使用 SQLite 3 的簡單例子就是下面這樣:

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()

注解

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

按需連接

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

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

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

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

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

簡化查詢

現(xiàn)在在每個請求處理函數(shù)里,您都可以訪問 g.db 來獲得當(dāng)前打開的數(shù)據(jù)庫連接。 此時,用一個輔助函數(shù)簡化 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ù)指針和連接對象。這個隨手即得的小函數(shù)讓操作數(shù)據(jù)庫的操作更為輕松。 像下面這樣使用它:

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

如果您只希望得到一個單獨的結(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ù)以鏈表的形式穿進去。 永遠不要直接將他們添加到 SQL 語句中以字符串形式傳入,這樣做將會允許惡意用戶 以 SQL 注入 的方式攻擊您的應(yīng)用。

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

關(guān)系數(shù)據(jù)庫需要一個模型來定義儲存數(shù)據(jù)的模式,所以應(yīng)用程序通常攜帶一個 schema.sql 文件用于創(chuàng)建數(shù)據(jù)庫。提供一個特定的函數(shù)來創(chuàng)建數(shù)據(jù)庫是個 不錯的主意,以下的函數(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)建一個這樣的數(shù)據(jù)庫:

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