|
|
|
@ -1,10 +1,10 @@ |
|
|
|
# SQL(關聯式)資料庫 { #sql-relational-databases } |
|
|
|
|
|
|
|
FastAPI 不強制你使用 SQL(關聯式)資料庫。你可以使用任何你想要的資料庫。 |
|
|
|
**FastAPI** 不強制你使用 SQL(關聯式)資料庫。但你可以使用**任何你想要的資料庫**。 |
|
|
|
|
|
|
|
這裡我們會用 [SQLModel](https://sqlmodel.tiangolo.com/) 作為範例。 |
|
|
|
|
|
|
|
SQLModel 建立在 [SQLAlchemy](https://www.sqlalchemy.org/) 與 Pydantic 之上。它由 FastAPI 的作者開發,非常適合需要使用 SQL 資料庫的 FastAPI 應用。 |
|
|
|
**SQLModel** 建立在 [SQLAlchemy](https://www.sqlalchemy.org/) 與 Pydantic 之上。它由 **FastAPI** 的作者開發,非常適合需要使用 **SQL 資料庫**的 FastAPI 應用。 |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
@ -12,7 +12,7 @@ SQLModel 建立在 [SQLAlchemy](https://www.sqlalchemy.org/) 與 Pydantic 之上 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
因為 SQLModel 建立在 SQLAlchemy 之上,你可以輕鬆使用 SQLAlchemy 所支援的任何資料庫(因此 SQLModel 也支援),例如: |
|
|
|
因為 SQLModel 建立在 SQLAlchemy 之上,你可以輕鬆使用 SQLAlchemy 所支援的**任何資料庫**(因此 SQLModel 也支援),例如: |
|
|
|
|
|
|
|
* PostgreSQL |
|
|
|
* MySQL |
|
|
|
@ -20,17 +20,17 @@ SQLModel 建立在 [SQLAlchemy](https://www.sqlalchemy.org/) 與 Pydantic 之上 |
|
|
|
* Oracle |
|
|
|
* Microsoft SQL Server,等等。 |
|
|
|
|
|
|
|
在這個範例中,我們會使用 SQLite,因為它只用到單一檔案,而且 Python 內建支援。你可以直接複製這個範例並原樣執行。 |
|
|
|
在這個範例中,我們會使用 **SQLite**,因為它只用到單一檔案,而且 Python 內建支援。你可以直接複製這個範例並原樣執行。 |
|
|
|
|
|
|
|
之後,在你的正式環境應用中,你可能會想使用像 PostgreSQL 這類的資料庫伺服器。 |
|
|
|
之後,在你的正式環境應用中,你可能會想使用像 **PostgreSQL** 這類的資料庫伺服器。 |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
有一個包含 FastAPI 與 PostgreSQL 的官方專案腳手架,還有前端與更多工具:[https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template) |
|
|
|
有一個包含 **FastAPI** 與 **PostgreSQL** 的官方專案產生器,還有前端與更多工具:[https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template) |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
這是一份非常簡短的教學,如果你想更全面學習資料庫、SQL,或更進階的功能,請參考 [SQLModel 文件](https://sqlmodel.tiangolo.com/)。 |
|
|
|
這是一份非常簡單且簡短的教學,如果你想更全面學習資料庫、SQL,或更進階的功能,請參考 [SQLModel 文件](https://sqlmodel.tiangolo.com/)。 |
|
|
|
|
|
|
|
## 安裝 `SQLModel` { #install-sqlmodel } |
|
|
|
|
|
|
|
@ -47,9 +47,9 @@ $ pip install sqlmodel |
|
|
|
|
|
|
|
## 建立只有單一模型的應用 { #create-the-app-with-a-single-model } |
|
|
|
|
|
|
|
我們先用單一 SQLModel 模型建立這個應用的最簡版。 |
|
|
|
我們先用單一 **SQLModel** 模型建立這個應用的最簡版。 |
|
|
|
|
|
|
|
接著我們會在下方用多個模型來提升安全性與彈性。🤓 |
|
|
|
接著我們會在下方用**多個模型**來提升安全性與彈性。🤓 |
|
|
|
|
|
|
|
### 建立模型 { #create-models } |
|
|
|
|
|
|
|
@ -57,43 +57,43 @@ $ pip install sqlmodel |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *} |
|
|
|
|
|
|
|
`Hero` 類別與 Pydantic 模型非常相似(事實上,在底層它就是一個 Pydantic 模型)。 |
|
|
|
`Hero` 類別與 Pydantic 模型非常相似(事實上,在底層它其實*就是一個 Pydantic 模型*)。 |
|
|
|
|
|
|
|
有幾點差異: |
|
|
|
|
|
|
|
* `table=True` 告訴 SQLModel 這是一個「資料表模型」(table model),它應該代表 SQL 資料庫中的一個資料表,而不僅僅是「資料模型」(就像一般的 Pydantic 類別)。 |
|
|
|
* `table=True` 告訴 SQLModel 這是一個*資料表模型*(table model),它應該代表 SQL 資料庫中的一個**資料表**,而不僅僅是*資料模型*(就像一般的 Pydantic 類別)。 |
|
|
|
|
|
|
|
* `Field(primary_key=True)` 告訴 SQLModel,`id` 是 SQL 資料庫中的「主鍵」。 (你可以在 SQLModel 文件中進一步了解 SQL 主鍵) |
|
|
|
* `Field(primary_key=True)` 告訴 SQLModel,`id` 是 SQL 資料庫中的**主鍵**(你可以在 SQLModel 文件中進一步了解 SQL 主鍵)。 |
|
|
|
|
|
|
|
注意:我們在主鍵欄位使用 `int | None`,這樣在 Python 程式碼中我們可以「在沒有 `id` 的情況下建立物件」(`id=None`),假設資料庫在儲存時會「自動產生」。SQLModel 瞭解資料庫會提供 `id`,並且在資料庫綱要中「將該欄位定義為非空的 `INTEGER`」。詳情請見 [SQLModel 文件:主鍵](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id)。 |
|
|
|
**注意:** 我們在主鍵欄位使用 `int | None`,這樣在 Python 程式碼中我們可以*在沒有 `id` 的情況下建立物件*(`id=None`),假設資料庫在儲存時會*自動產生*。SQLModel 瞭解資料庫會提供 `id`,並且在資料庫綱要中*將該欄位定義為非空的 `INTEGER`*。詳情請見 [SQLModel 文件:主鍵](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id)。 |
|
|
|
|
|
|
|
* `Field(index=True)` 告訴 SQLModel 應為此欄位建立「SQL 索引」,以便在用此欄位過濾讀取資料時更快查詢。 |
|
|
|
* `Field(index=True)` 告訴 SQLModel 應為此欄位建立 **SQL 索引**,以便在用此欄位過濾讀取資料時更快查詢。 |
|
|
|
|
|
|
|
SQLModel 會知道宣告為 `str` 的欄位在 SQL 中會是 `TEXT`(或 `VARCHAR`,依資料庫而定)。 |
|
|
|
|
|
|
|
### 建立引擎 { #create-an-engine } |
|
|
|
|
|
|
|
SQLModel 的 `engine`(底層實際上是 SQLAlchemy 的 `engine`)是用來「維護與資料庫連線」的東西。 |
|
|
|
SQLModel 的 `engine`(底層實際上是 SQLAlchemy 的 `engine`)是用來**維護與資料庫連線**的東西。 |
|
|
|
|
|
|
|
你的程式中應該只有「單一 `engine` 物件」來連到同一個資料庫。 |
|
|
|
你的程式中應該只有**單一 `engine` 物件**來連到同一個資料庫。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *} |
|
|
|
|
|
|
|
使用 `check_same_thread=False` 允許 FastAPI 在不同執行緒中使用同一個 SQLite 資料庫。這是必要的,因為「單一請求」可能會使用「多個執行緒」(例如在依賴項中)。 |
|
|
|
使用 `check_same_thread=False` 允許 FastAPI 在不同執行緒中使用同一個 SQLite 資料庫。這是必要的,因為**單一請求**可能會使用**多個執行緒**(例如在依賴項中)。 |
|
|
|
|
|
|
|
別擔心,依照我們的程式結構,稍後我們會確保「每個請求只使用單一 SQLModel 的 session」,這其實就是 `check_same_thread` 想要達成的事。 |
|
|
|
別擔心,依照我們的程式結構,稍後我們會確保**每個請求只使用單一 SQLModel 的 *session***,這其實就是 `check_same_thread` 想要達成的事。 |
|
|
|
|
|
|
|
### 建立資料表 { #create-the-tables } |
|
|
|
|
|
|
|
接著我們新增一個函式,使用 `SQLModel.metadata.create_all(engine)` 為所有「資料表模型」建立資料表。 |
|
|
|
接著我們新增一個函式,使用 `SQLModel.metadata.create_all(engine)` 為所有*資料表模型* **建立資料表**。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *} |
|
|
|
|
|
|
|
### 建立 Session 依賴 { #create-a-session-dependency } |
|
|
|
|
|
|
|
「`Session`」會在記憶體中保存物件並追蹤資料需要的任何變更,然後透過「`engine`」與資料庫溝通。 |
|
|
|
**`Session`** 會在記憶體中保存**物件**並追蹤資料需要的任何變更,然後透過 **`engine`** 與資料庫溝通。 |
|
|
|
|
|
|
|
我們會用 `yield` 建立一個 FastAPI 的「依賴」,為每個請求提供一個新的 `Session`。這可確保每個請求只使用單一的 session。🤓 |
|
|
|
我們會用 `yield` 建立一個 FastAPI 的**依賴**,為每個請求提供一個新的 `Session`。這可確保每個請求只使用單一的 session。🤓 |
|
|
|
|
|
|
|
接著我們建立一個 `Annotated` 的依賴 `SessionDep`,讓後續使用這個依賴的程式碼更簡潔。 |
|
|
|
|
|
|
|
@ -117,11 +117,11 @@ SQLModel 之後會提供包裝 Alembic 的遷移工具,但目前你可以直 |
|
|
|
|
|
|
|
### 建立 Hero { #create-a-hero } |
|
|
|
|
|
|
|
因為每個 SQLModel 模型同時也是一個 Pydantic 模型,你可以在「型別標註」中像使用 Pydantic 模型一樣使用它。 |
|
|
|
因為每個 SQLModel 模型同時也是一個 Pydantic 模型,你可以在與 Pydantic 模型相同的**型別標註**中使用它。 |
|
|
|
|
|
|
|
例如,如果你宣告一個參數型別為 `Hero`,它會從「JSON body」中讀取。 |
|
|
|
例如,如果你宣告一個參數型別為 `Hero`,它會從 **JSON body** 中讀取。 |
|
|
|
|
|
|
|
同樣地,你也可以將它宣告為函式的「回傳型別」,然後在自動產生的 API 文件 UI 中就會顯示其資料結構。 |
|
|
|
同樣地,你也可以將它宣告為函式的**回傳型別**,然後在自動產生的 API 文件 UI 中就會顯示其資料結構。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *} |
|
|
|
|
|
|
|
@ -129,19 +129,19 @@ SQLModel 之後會提供包裝 Alembic 的遷移工具,但目前你可以直 |
|
|
|
|
|
|
|
### 讀取多個 Hero { #read-heroes } |
|
|
|
|
|
|
|
我們可以用 `select()` 從資料庫「讀取」多個 `Hero`。可以加入 `limit` 與 `offset` 來分頁。 |
|
|
|
我們可以用 `select()` 從資料庫**讀取**多個 `Hero`。可以加入 `limit` 與 `offset` 來分頁。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *} |
|
|
|
|
|
|
|
### 讀取單一 Hero { #read-one-hero } |
|
|
|
|
|
|
|
我們可以「讀取」單一的 `Hero`。 |
|
|
|
我們可以**讀取**單一的 `Hero`。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *} |
|
|
|
|
|
|
|
### 刪除 Hero { #delete-a-hero } |
|
|
|
|
|
|
|
我們也可以「刪除」一個 `Hero`。 |
|
|
|
我們也可以**刪除**一個 `Hero`。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *} |
|
|
|
|
|
|
|
@ -159,7 +159,7 @@ $ fastapi dev |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
然後前往 `/docs` 的 UI,你會看到 FastAPI 使用這些模型來「文件化」API,也會用它們來「序列化」與「驗證」資料。 |
|
|
|
然後前往 `/docs` 的 UI,你會看到 **FastAPI** 使用這些**模型**來**文件化** API,也會用它們來**序列化**與**驗證**資料。 |
|
|
|
|
|
|
|
<div class="screenshot"> |
|
|
|
<img src="/img/tutorial/sql-databases/image01.png"> |
|
|
|
@ -167,27 +167,27 @@ $ fastapi dev |
|
|
|
|
|
|
|
## 用多個模型更新應用 { #update-the-app-with-multiple-models } |
|
|
|
|
|
|
|
現在我們稍微「重構」一下這個應用,以提升「安全性」與「彈性」。 |
|
|
|
現在我們稍微**重構**一下這個應用,以提升**安全性**與**彈性**。 |
|
|
|
|
|
|
|
如果你檢查前一版的應用,在 UI 中你會看到,到目前為止它讓用戶端自己決定要建立的 `Hero` 的 `id`。😱 |
|
|
|
|
|
|
|
我們不該允許這樣,因為他們可能會覆蓋資料庫中我們已分配的 `id`。決定 `id` 應該由「後端」或「資料庫」來做,「不是用戶端」。 |
|
|
|
我們不該允許這樣,因為他們可能會覆蓋資料庫中我們已分配的 `id`。決定 `id` 應該由**後端**或**資料庫**來做,**不是用戶端**。 |
|
|
|
|
|
|
|
另外,我們為 hero 建立了 `secret_name`,但目前我們在各處都把它回傳出去,這一點都不「保密」... 😅 |
|
|
|
另外,我們為 hero 建立了 `secret_name`,但目前我們在各處都把它回傳出去,這一點都不**保密**... 😅 |
|
|
|
|
|
|
|
我們會透過加入一些「額外模型」來修正這些問題。這正是 SQLModel 大放異彩的地方。✨ |
|
|
|
我們會透過加入一些**額外模型**來修正這些問題。這正是 SQLModel 大放異彩的地方。✨ |
|
|
|
|
|
|
|
### 建立多個模型 { #create-multiple-models } |
|
|
|
|
|
|
|
在 SQLModel 中,任何設了 `table=True` 的模型類別都是「資料表模型」。 |
|
|
|
在 **SQLModel** 中,任何設了 `table=True` 的模型類別都是**資料表模型**。 |
|
|
|
|
|
|
|
而沒有設 `table=True` 的模型類別就是「資料模型」,這些其實就是 Pydantic 模型(只有一點小增強)。🤓 |
|
|
|
而沒有設 `table=True` 的模型類別就是**資料模型**,這些其實就是 Pydantic 模型(只有一點小增強)。🤓 |
|
|
|
|
|
|
|
使用 SQLModel,我們可以利用「繼承」來「避免重複」在各種情況下一再宣告所有欄位。 |
|
|
|
使用 SQLModel,我們可以利用**繼承**來**避免重複**在各種情況下一再宣告所有欄位。 |
|
|
|
|
|
|
|
#### `HeroBase` - 基底類別 { #herobase-the-base-class } |
|
|
|
|
|
|
|
先從 `HeroBase` 模型開始,它包含所有模型「共享」的欄位: |
|
|
|
先從 `HeroBase` 模型開始,它包含所有模型**共享**的欄位: |
|
|
|
|
|
|
|
* `name` |
|
|
|
* `age` |
|
|
|
@ -196,12 +196,12 @@ $ fastapi dev |
|
|
|
|
|
|
|
#### `Hero` - 資料表模型 { #hero-the-table-model } |
|
|
|
|
|
|
|
接著建立 `Hero`,也就是實際的「資料表模型」,它包含不一定會出現在其他模型中的「額外欄位」: |
|
|
|
接著建立 `Hero`,也就是實際的*資料表模型*,它包含不一定會出現在其他模型中的**額外欄位**: |
|
|
|
|
|
|
|
* `id` |
|
|
|
* `secret_name` |
|
|
|
|
|
|
|
因為 `Hero` 繼承自 `HeroBase`,它「也」擁有 `HeroBase` 中宣告的「欄位」,因此 `Hero` 的完整欄位為: |
|
|
|
因為 `Hero` 繼承自 `HeroBase`,它**也**擁有 `HeroBase` 中宣告的**欄位**,因此 `Hero` 的完整欄位為: |
|
|
|
|
|
|
|
* `id` |
|
|
|
* `name` |
|
|
|
@ -212,19 +212,19 @@ $ fastapi dev |
|
|
|
|
|
|
|
#### `HeroPublic` - 公開的資料模型 { #heropublic-the-public-data-model } |
|
|
|
|
|
|
|
接下來建立 `HeroPublic` 模型,它是要「回傳」給 API 用戶端的模型。 |
|
|
|
接下來建立 `HeroPublic` 模型,它是要**回傳**給 API 用戶端的模型。 |
|
|
|
|
|
|
|
它擁有與 `HeroBase` 相同的欄位,因此不會包含 `secret_name`。 |
|
|
|
|
|
|
|
終於,我們英雄的真實身分受保護了!🥷 |
|
|
|
|
|
|
|
它也重新宣告了 `id: int`。這麼做是與 API 用戶端訂立一個「契約」,讓他們可以確定 `id` 一定存在而且是 `int`(不會是 `None`)。 |
|
|
|
它也重新宣告了 `id: int`。這麼做是與 API 用戶端訂立一個**契約**,讓他們可以確定 `id` 一定存在而且是 `int`(不會是 `None`)。 |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
讓回傳模型保證某個值一定存在、而且一定是 `int`(不是 `None`),對 API 用戶端非常有幫助。他們在有這個確信下可以寫出更簡單的程式碼。 |
|
|
|
|
|
|
|
此外,透過「自動產生的客戶端」也會有更簡潔的介面,讓要使用你 API 的開發者能有更好的開發體驗。😎 |
|
|
|
此外,透過**自動產生的客戶端**也會有更簡潔的介面,讓要使用你 API 的開發者能有更好的開發體驗。😎 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
@ -238,17 +238,17 @@ $ fastapi dev |
|
|
|
|
|
|
|
#### `HeroCreate` - 用於建立 Hero 的資料模型 { #herocreate-the-data-model-to-create-a-hero } |
|
|
|
|
|
|
|
現在我們建立 `HeroCreate` 模型,這是用來「驗證」用戶端送來資料的模型。 |
|
|
|
現在我們建立 `HeroCreate` 模型,這是用來**驗證**用戶端送來資料的模型。 |
|
|
|
|
|
|
|
它具有與 `HeroBase` 相同的欄位,並且還有 `secret_name`。 |
|
|
|
|
|
|
|
接下來,當用戶端「建立新 hero」時,他們會送上 `secret_name`,它會被儲存在資料庫中,但這些祕密名稱不會在 API 中回傳給用戶端。 |
|
|
|
接下來,當用戶端**建立新 hero** 時,他們會送上 `secret_name`,它會被儲存在資料庫中,但這些祕密名稱不會在 API 中回傳給用戶端。 |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
這也就是你處理「密碼」的方式。接收它們,但不要在 API 中回傳。 |
|
|
|
這也就是你處理**密碼**的方式。接收它們,但不要在 API 中回傳。 |
|
|
|
|
|
|
|
你也應該在儲存前先對密碼做「雜湊」,「永遠不要以明文儲存」。 |
|
|
|
你也應該在儲存前先對密碼做**雜湊**,**永遠不要以明文儲存**。 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
@ -262,11 +262,11 @@ $ fastapi dev |
|
|
|
|
|
|
|
#### `HeroUpdate` - 用於更新 Hero 的資料模型 { #heroupdate-the-data-model-to-update-a-hero } |
|
|
|
|
|
|
|
在前一版的應用中,我們沒有「更新 hero」的方式,但現在有了「多個模型」,我們就能做到。🎉 |
|
|
|
在前一版的應用中,我們沒有**更新 hero** 的方式,但現在有了**多個模型**,我們就能做到。🎉 |
|
|
|
|
|
|
|
`HeroUpdate` 這個資料模型有點特別,它包含「建立新 hero 所需的所有欄位」,但所有欄位都是「可選的」(都有預設值)。這樣在更新時,你只需要送出想要更新的欄位即可。 |
|
|
|
`HeroUpdate` 這個*資料模型*有點特別,它包含**建立新 hero 所需的所有欄位**,但所有欄位都是**可選的**(都有預設值)。這樣在更新時,你只需要送出想要更新的欄位即可。 |
|
|
|
|
|
|
|
因為所有欄位的「型別其實都改變了」(型別現在包含 `None`,而且預設值為 `None`),我們需要「重新宣告」它們。 |
|
|
|
因為所有**欄位其實都改變了**(型別現在包含 `None`,而且預設值為 `None`),我們需要**重新宣告**它們。 |
|
|
|
|
|
|
|
其實不一定要繼承 `HeroBase`,因為我們會重新宣告所有欄位。我這裡保留繼承只是為了一致性,並非必要。這主要是個人偏好的問題。🤷 |
|
|
|
|
|
|
|
@ -280,43 +280,43 @@ $ fastapi dev |
|
|
|
|
|
|
|
### 用 `HeroCreate` 建立並回傳 `HeroPublic` { #create-with-herocreate-and-return-a-heropublic } |
|
|
|
|
|
|
|
現在我們有了「多個模型」,可以更新應用中使用它們的部分。 |
|
|
|
現在我們有了**多個模型**,可以更新應用中使用它們的部分。 |
|
|
|
|
|
|
|
我們在請求中接收 `HeroCreate`(資料模型),並由它建立一個 `Hero`(資料表模型)。 |
|
|
|
我們在請求中接收 `HeroCreate` *資料模型*,並由它建立一個 `Hero` *資料表模型*。 |
|
|
|
|
|
|
|
這個新的資料表模型 `Hero` 會有用戶端傳來的欄位,並且會由資料庫產生一個 `id`。 |
|
|
|
這個新的*資料表模型* `Hero` 會有用戶端傳來的欄位,並且會由資料庫產生一個 `id`。 |
|
|
|
|
|
|
|
然後我們直接從函式回傳這個資料表模型 `Hero`。但因為我們用 `HeroPublic` 當作 `response_model`,FastAPI 會用 `HeroPublic` 來驗證與序列化資料。 |
|
|
|
然後我們直接從函式回傳這個*資料表模型* `Hero`。但因為我們用 `HeroPublic` *資料模型*當作 `response_model`,**FastAPI** 會用 `HeroPublic` 來驗證與序列化資料。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *} |
|
|
|
|
|
|
|
/// tip | 提示 |
|
|
|
|
|
|
|
現在我們用 `response_model=HeroPublic`,而不是用回傳型別標註 `-> HeroPublic`,因為我們實際回傳的值其實「不是」`HeroPublic`。 |
|
|
|
現在我們用 `response_model=HeroPublic`,而不是用**回傳型別標註** `-> HeroPublic`,因為我們實際回傳的值其實*不是* `HeroPublic`。 |
|
|
|
|
|
|
|
如果我們宣告 `-> HeroPublic`,你的編輯器與 linter 會(理所當然地)抱怨你回傳的是 `Hero` 而不是 `HeroPublic`。 |
|
|
|
|
|
|
|
在 `response_model` 中宣告,就是要讓 FastAPI 去做它該做的事,而不影響型別標註,以及你的編輯器與其他工具提供的協助。 |
|
|
|
在 `response_model` 中宣告,就是要讓 **FastAPI** 去做它該做的事,而不影響型別標註,以及你的編輯器與其他工具提供的協助。 |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
### 使用 `HeroPublic` 讀取多個 Hero { #read-heroes-with-heropublic } |
|
|
|
|
|
|
|
我們可以像先前一樣「讀取」多個 `Hero`。同樣地,我們使用 `response_model=list[HeroPublic]` 來確保資料被正確驗證與序列化。 |
|
|
|
我們可以像先前一樣**讀取**多個 `Hero`。同樣地,我們使用 `response_model=list[HeroPublic]` 來確保資料被正確驗證與序列化。 |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *} |
|
|
|
|
|
|
|
### 使用 `HeroPublic` 讀取單一 Hero { #read-one-hero-with-heropublic } |
|
|
|
|
|
|
|
我們可以「讀取」單一 hero: |
|
|
|
我們可以**讀取**單一 hero: |
|
|
|
|
|
|
|
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *} |
|
|
|
|
|
|
|
### 使用 `HeroUpdate` 更新 Hero { #update-a-hero-with-heroupdate } |
|
|
|
|
|
|
|
我們可以「更新 hero」。為此我們使用 HTTP 的 `PATCH` 操作。 |
|
|
|
我們可以**更新 hero**。為此我們使用 HTTP 的 `PATCH` 操作。 |
|
|
|
|
|
|
|
在程式碼中,我們會取得一個只包含用戶端有傳送的資料的 `dict`,不包含只是因為有預設值而存在的欄位。為了達成這點,我們使用 `exclude_unset=True`。這是關鍵。🪄 |
|
|
|
在程式碼中,我們會取得一個只包含用戶端有傳送的資料的 `dict`,**只包含用戶端傳送的資料**,不包含只是因為有預設值而存在的欄位。為了達成這點,我們使用 `exclude_unset=True`。這是關鍵。🪄 |
|
|
|
|
|
|
|
然後我們使用 `hero_db.sqlmodel_update(hero_data)` 以 `hero_data` 的資料更新 `hero_db`。 |
|
|
|
|
|
|
|
@ -324,7 +324,7 @@ $ fastapi dev |
|
|
|
|
|
|
|
### 再次刪除 Hero { #delete-a-hero-again } |
|
|
|
|
|
|
|
「刪除」 hero 基本上維持不變。 |
|
|
|
**刪除** hero 基本上維持不變。 |
|
|
|
|
|
|
|
我們不會為了重構而重構一切。😅 |
|
|
|
|
|
|
|
@ -352,6 +352,6 @@ $ fastapi dev |
|
|
|
|
|
|
|
## 總結 { #recap } |
|
|
|
|
|
|
|
你可以使用 [SQLModel](https://sqlmodel.tiangolo.com/) 與 SQL 資料庫互動,並用「資料模型」與「資料表模型」讓程式碼更簡潔。 |
|
|
|
你可以使用 [**SQLModel**](https://sqlmodel.tiangolo.com/) 與 SQL 資料庫互動,並用*資料模型*與*資料表模型*讓程式碼更簡潔。 |
|
|
|
|
|
|
|
你可以在 SQLModel 文件學到更多內容,這裡還有一份更長的 [使用 SQLModel 與 FastAPI 的教學](https://sqlmodel.tiangolo.com/tutorial/fastapi/)。🚀 |
|
|
|
你可以在 **SQLModel** 文件學到更多內容,這裡還有一份更長的 [使用 SQLModel 與 **FastAPI** 的教學](https://sqlmodel.tiangolo.com/tutorial/fastapi/)。🚀 |
|
|
|
|