搭配 Flask 使用 SQLite 3

在 Flask 中,您可以輕鬆實作依需求開啟資料庫連線,並在情境結束時(通常在請求結束時)關閉連線。

以下是如何搭配 Flask 使用 SQLite 3 的簡單範例

import sqlite3
from flask import g

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

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(DATABASE)
    return db

@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

現在,要使用資料庫,應用程式必須具有作用中的應用程式情境(在請求進行中時始終為真),或自行建立應用程式情境。在那時,可以使用 get_db 函數來取得目前的資料庫連線。每當情境被銷毀時,資料庫連線將會終止。

範例

@app.route('/')
def index():
    cur = get_db().cursor()
    ...

注意

請記住,即使 before-request 處理器失敗或從未執行,teardown request 和 appcontext 函數始終會執行。因此,我們必須在此處確保資料庫存在,然後再關閉它。

依需求連線

這種方法(在首次使用時連線)的優點是,只有在真正需要時才會開啟連線。如果您想在請求情境之外使用此程式碼,您可以在 Python shell 中手動開啟應用程式情境來使用它

with app.app_context():
    # now you can use get_db()

簡易查詢

現在,在每個請求處理函數中,您可以存取 get_db() 以取得目前開啟的資料庫連線。為了簡化使用 SQLite,row factory 函數非常有用。它會針對從資料庫傳回的每個結果執行,以轉換結果。例如,為了取得字典而不是元組,可以將其插入到我們上面建立的 get_db 函數中

def make_dicts(cursor, row):
    return dict((cursor.description[idx][0], value)
                for idx, value in enumerate(row))

db.row_factory = make_dicts

這將使 sqlite3 模組為此資料庫連線傳回 dict,這更容易處理。更簡單的是,我們可以將其放在 get_db

db.row_factory = sqlite3.Row

這將使用 Row 物件而不是 dict 來傳回查詢結果。這些是 namedtuple,因此我們可以通過索引或鍵來存取它們。例如,假設我們有一個名為 rsqlite3.Row,用於列 idFirstNameLastNameMiddleInitial

>>> # You can get values based on the row's name
>>> r['FirstName']
John
>>> # Or, you can get them based on index
>>> r[1]
John
# Row objects are also iterable:
>>> for value in r:
...     print(value)
1
John
Doe
M

此外,提供一個查詢函數是個好主意,該函數結合了取得游標、執行和提取結果

def query_db(query, args=(), one=False):
    cur = get_db().execute(query, args)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv

這個方便的小函數,結合 row factory,使使用資料庫比僅使用原始游標和連線物件更令人愉快。

以下是如何使用它

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

或者,如果您只需要單一結果

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 陳述式,請在陳述式中使用問號,並將引數作為列表傳遞。永遠不要使用字串格式化將它們直接添加到 SQL 陳述式中,因為這可能會導致使用 SQL Injection 攻擊應用程式。

初始結構描述

關聯式資料庫需要結構描述,因此應用程式通常會附帶一個 schema.sql 檔案來建立資料庫。提供一個基於該結構描述建立資料庫的函數是個好主意。此函數可以為您做到這一點

def init_db():
    with app.app_context():
        db = get_db()
        with app.open_resource('schema.sql', mode='r') as f:
            db.cursor().executescript(f.read())
        db.commit()

然後,您可以從 Python shell 建立這樣的資料庫

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