定義與存取資料庫¶
此應用程式將使用 SQLite 資料庫來儲存使用者和貼文。Python 在 sqlite3
模組中內建支援 SQLite。
SQLite 很方便,因為它不需要設定獨立的資料庫伺服器,而且是 Python 內建的。然而,如果同時有多個請求嘗試寫入資料庫,它們會因為每次寫入循序發生而變慢。小型應用程式不會注意到這一點。一旦您的應用程式變大,您可能會想要切換到不同的資料庫。
本教學不會深入探討 SQL 的細節。如果您不熟悉 SQL,SQLite 文件描述了其語言。
連線到資料庫¶
使用 SQLite 資料庫(以及大多數其他 Python 資料庫函式庫)時,首先要做的是建立與資料庫的連線。任何查詢和操作都使用此連線執行,並在工作完成後關閉。
在 Web 應用程式中,此連線通常與請求綁定。它在處理請求的某個時間點建立,並在傳送回應之前關閉。
flaskr/db.py
¶import sqlite3
from datetime import datetime
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.')
sqlite3.register_converter(
"timestamp", lambda v: datetime.fromisoformat(v.decode())
)
open_resource()
開啟相對於 flaskr
套件的檔案,這很有用,因為您不一定知道稍後部署應用程式時該位置在哪裡。get_db
傳回資料庫連線,用於執行從檔案讀取的命令。
click.command()
定義了一個名為 init-db
的命令列指令,它呼叫 init_db
函式並向使用者顯示成功訊息。您可以閱讀 命令列介面 以了解有關編寫命令的更多資訊。
呼叫 sqlite3.register_converter()
告訴 Python 如何解釋資料庫中的時間戳記值。我們將值轉換為 datetime.datetime
。
向應用程式註冊¶
close_db
和 init_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.
現在專案的 instance
資料夾中將會有一個 flaskr.sqlite
檔案。
繼續前往 藍圖與視圖。