搭配 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
,因此我們可以通過索引或鍵來存取它們。例如,假設我們有一個名為 r
的 sqlite3.Row
,用於列 id
、FirstName
、LastName
和 MiddleInitial
>>> # 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()