FastAPI 教學 (Visual Studio Code)
FastAPI 是一個現代化、高效能的 Web 框架,用於使用 Python 建置 API。它旨在讓使用者能夠輕鬆快速且有效率地建置 API,同時提供 API 的自動驗證、序列化和文件產生等功能,使其成為建置 Web 服務和微服務的熱門選擇。
在本 FastAPI 教學課程中,我們將使用 FastAPI 建立一個雜貨清單應用程式。在本教學課程結束時,您將了解如何在 Visual Studio Code 終端機、編輯器和偵錯工具中使用 FastAPI。本教學課程並非 FastAPI 的深入探討。如需深入了解,您可以參考 官方 FastAPI 文件。
如果您是第一次使用 Python,我們建議您從我們的 Python 教學課程 開始,以熟悉該語言和 VS Code 的 Python 支援。本教學課程更適合已經熟悉 Python 且想要學習如何在 VS Code 中使用 FastAPI 的使用者。
本 FastAPI 教學課程的完整程式碼專案可以在 GitHub 上找到:python-sample-vscode-fastapi-tutorial。
如果您有任何問題,可以在 Python 擴充功能討論問答區 搜尋解答或提出問題。
設定專案
您可以使用不同的方式設定此教學課程的專案。我們將介紹如何在 GitHub Codespaces 和 本機電腦上的 VS Code 中設定。
GitHub Codespaces
您可以設定此專案以在 GitHub Codespaces 中開發,您可以在其中於程式碼空間中遠端撰寫程式碼、偵錯和執行您的應用程式。程式碼空間提供完全設定的開發環境,託管在雲端中,無需在本機進行設定。此環境包含專案的相依性、工具和擴充功能,確保一致且可重現的開發體驗。它透過提供即時編輯、整合的版本控制以及輕鬆存取偵錯和測試工具,簡化了協作,同時保持專案的安全性和可靠性。
注意:所有 GitHub.com 帳戶在 Free 或 Pro 方案中都包含每月免費使用 GitHub Codespaces 的配額。如需更多資訊,請前往 關於 GitHub Codespaces 的計費。
若要為本教學課程設定程式碼空間,請導覽至 此專案的 GitHub 存放庫。此程式碼空間包含所有必要的組態和相依性,可讓您快速開始 FastAPI 開發。
針對本教學課程,請選取 dictionarybased 分支
然後,選取 程式碼 > Codespaces > 在 <dictionarybased> 分支上建立 Codespace 以建立並開啟專案的程式碼空間。
完成後,您可以繼續進行下方的 取代資料庫 區段。
在本機 VS Code 中
若要成功完成本教學課程,您首先需要在 VS Code 中設定 Python 開發環境。具體而言,本教學課程需要
- Python 3 (如果您尚未安裝,請查看安裝指南)
- VS Code 的 Python 擴充功能 (如需安裝擴充功能的其他詳細資訊,您可以閱讀 擴充功能市集)。
在本節中,我們將建立一個資料夾以作為 VS Code 中的工作區開啟、設定 Python 虛擬環境,並安裝專案的相依性。
-
在您的檔案系統中,為本教學課程建立一個專案資料夾,例如
groceries-plugin
。 -
在 VS Code 中開啟這個新資料夾 (檔案 > 開啟資料夾…)。
-
當 工作區信任 提示出現時,選取 是,我信任作者,以允許工作區存取必要的資源和擴充功能。您可以在 文件 中了解更多關於工作區信任的資訊。
現在,讓我們建立一個 requirements.txt
檔案,其中列出我們希望為應用程式安裝的相依性。requirements.txt
檔案是 Python 開發中的常見做法,用於指定專案所依賴的程式庫及其版本。此檔案有助於確保任何參與專案的人員都可以重新建立類似的開發環境,使其成為維護一致性的便利元件。
我們將安裝 FastAPI 以建立應用程式、uvicorn 作為伺服器,以及 Redis 和 type-redis
以處理資料儲存並與 Redis 資料庫互動。
-
在 VS Code 中建立新檔案 (檔案 > 新增文字檔 或 ⌘N (Windows、Linux Ctrl+N))。
-
將以下內容新增至其中
fastapi redis types-redis uvicorn
-
儲存檔案 (⌘S (Windows、Linux Ctrl+S)) 並將其命名為
requirements.txt
。 -
透過開啟命令面板 (⇧⌘P (Windows、Linux Ctrl+Shift+P)) 並執行 Python: 建立環境 命令來建立虛擬環境。
注意:此步驟可能需要幾分鐘才能完成。
-
當詢問環境類型時,選取 Venv
-
然後選取您機器上可用的最新 Python 版本
-
從下拉式清單中選取
requirements.txt
檔案,以便自動安裝相依性,然後選取 確定
將會建立虛擬環境,自動安裝相依性,並選取環境供 Python 擴充功能在您的工作區中使用。您可以透過檢查 VS Code 的右下角來確認它已被選取
注意:如果您在狀態列上找不到新建立的環境資訊,您可以按一下 Python 解譯器指示器 (或從命令面板執行 Python: 選取解譯器 命令) 並手動選取虛擬環境。
開始撰寫程式碼
讓我們建立應用程式!
-
透過使用 檔案 > 新增檔案…,然後選取 Python 檔案,來建立新的 Python 檔案。
-
將其儲存為
main.py
(⇧⌘S (Windows、Linux Ctrl+Shift+S)) 在groceries-plugin
資料夾中。 -
將以下程式碼新增至
main.py
並儲存檔案from fastapi import FastAPI app = FastAPI() @app.get("/") def root(): return {"message": "Hello World"}
-
透過啟動偵錯工具 (F5) 來執行程式碼。
-
從下拉式功能表中,從清單中選取 FastAPI 組態選項
這會自動建立一個偵錯組態,該組態會呼叫 uvicorn 以透過偵錯工具啟動應用程式伺服器,並讓您逐步執行原始碼以檢查其行為。您應該會在終端機中看到類似以下內容
提示:如果您的預設連接埠已被使用,請停止偵錯工具並開啟命令面板 (⇧⌘P (Windows、Linux Ctrl+Shift+P)),搜尋 偵錯: 新增組態,選取 Python 偵錯工具,然後選取 FastAPI。這會在
.vscode/launch.json
中建立一個自訂組態檔,您可以對其進行編輯。將以下內容新增至"args":[]
以設定自訂連接埠:"--port=5000"
。儲存檔案,然後使用 (F5) 重新啟動偵錯工具。 -
Ctrl+按一下 終端機中的
http://127.0.0.1:8000/
URL,以在您的預設瀏覽器中開啟該位址恭喜!您的 FastAPI 應用程式已啟動並執行!
-
使用偵錯工具列中的 停止 按鈕,或透過 ⇧F5 (Windows、Linux Shift+F5) 來停止偵錯工具。
建立雜貨清單項目的模型
現在我們已經讓 FastAPI 應用程式正常運作,我們可以透過使用 Pydantic 來定義我們的雜貨清單項目,Pydantic 是一個資料驗證和剖析程式庫,可與 FastAPI 無縫整合。Pydantic 可讓您使用具有 類型提示 的 Python 類別定義資料模型,以自動驗證和剖析 API 請求中的傳入資料 (稱為「酬載」)。
讓我們為我們的雜貨清單項目建立一個模型。我們將使用 ItemPayload
模型來定義要新增至雜貨清單的項目的資料結構。此模型將具有三個欄位:item_id
、item_name
和 quantity
。
-
透過 檔案 > 新增檔案…,然後選取 Python 檔案 來建立新的 Python 檔案。
-
將以下幾行新增至檔案,然後將其儲存在
groceries-plugin
資料夾中,並命名為models.py
(⇧⌘S (Windows、Linux Ctrl+Shift+S))from typing import Optional from pydantic import BaseModel class ItemPayload(BaseModel): item_id: Optional[int] item_name: str quantity: int
Pylance 是 VS Code 中 Python 的預設語言伺服器,它支援類型提示功能,這些功能對於使用 Pydantic 模型和 FastAPI 可能很有幫助。這是因為 Pylance 建構於 Pyright 之上,Pyright 是 Python 的靜態類型檢查器,可以偵測程式碼中的類型錯誤,以防止錯誤並提高程式碼品質。
以下三個步驟是選用的,但鑑於 FastAPI 廣泛使用類型提示來提高程式碼可讀性和驗證,我們可以利用 Pylance 的類型檢查功能來儘早發現錯誤
-
開啟設定編輯器 (⌘, (Windows、Linux Ctrl+,))。
-
搜尋「python type checking mode」,並將其設定為
basic
以進行基本類型檢查。Pylance 現在將顯示診斷和警告,以捕捉簡單的類型相關錯誤。或者,您可以將其設定為strict
以強制執行更進階的 類型檢查規則。 -
接下來,搜尋「Python inlay type hints」,並為 變數類型 和 函式傳回類型 啟用內嵌提示
建立路由
現在我們需要一個地方來儲存雜貨清單項目。為了簡單起見,我們先從一個空的字典開始。
-
首先,讓我們匯入範例所需的所有套件。開啟
main.py
檔案,並將第一個匯入行取代為以下幾行from fastapi import FastAPI, HTTPException from models import ItemPayload
-
現在在
app = FastAPI()
下方新增以下行grocery_list: dict[int, ItemPayload] = {}
這會建立一個新的空字典,它接收
int
類型的鍵 (作為項目 ID) 和ItemPayload
類型的值。我們現在將在我們的 FastAPI 應用程式中定義路由。在 Web 應用程式的上下文中,路由就像路徑一樣,將特定的 URL 對應到處理它們的程式碼。這些路由充當我們應用程式中不同功能的進入點。當用戶端 (例如 Web 瀏覽器或其他程式) 向我們的應用程式發送具有特定 URL 的請求時,FastAPI 會根據 URL 將該請求路由到適當的函式 (也稱為路由處理常式或檢視函式),並且該函式會處理請求並產生回應。
讓我們繼續定義路由以新增和擷取個別項目,以及傳回雜貨清單中的所有項目。
-
在
main.py
檔案的結尾新增以下路由# Route to add a item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int): if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # if item already exists, we'll just add the quantity. # get all item names items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} if item_name in items_ids.keys(): # get index of item_name in item_ids, which is the item_id item_id = items_ids[item_name] grocery_list[item_id].quantity += quantity # otherwise, create a new item else: # generate an ID for the item based on the highest ID in the grocery_list item_id = max(grocery_list.keys()) + 1 if grocery_list else 0 grocery_list[item_id] = ItemPayload( item_id=item_id, item_name=item_name, quantity=quantity ) return {"item": grocery_list[item_id]}
如果您已在先前的章節中啟用類型提示,您可能會注意到 Pylance 新增了函式傳回類型的內嵌提示,以及
item_ids
和item_id
的類型。您可以選擇性地按兩下每個建議以將其插入程式碼中現在讓我們檢查此路由是否如預期般運作。最快的方法是同時使用 VS Code 的偵錯工具以及 FastAPI 的
/docs
端點,它提供有關所有可用 API 路由的資訊,並讓您與 API 互動以探索其參數和回應。此文件是根據 FastAPI 應用程式中定義的中繼資料和類型提示動態產生的。 -
透過按一下行號的左邊界 (或 F9),在
if quantity <= 0
陳述式旁邊新增中斷點。偵錯工具將在執行該行之前停止,以便您可以逐行檢查程式碼。 -
啟動偵錯工具 (F5),然後在瀏覽器中導覽至
http://127.0.0.1:8000/docs
。應該會有一個 Swagger 介面,其中包含應用程式中可用的兩個端點:
/items
和根目錄 (/
)。 -
選取
/items
路由旁邊的向下箭頭以展開它,然後選取右側出現的 試用看看 按鈕。 -
透過將字串傳遞至
item_name
欄位和數字傳遞至quantity
來新增雜貨清單項目。例如,您可以提供 apple 作為item_name
,並提供 2 作為quantity
。 -
選取 執行。
-
再次開啟 VS Code,並注意偵錯工具已在您先前設定的中斷點停止。
在左側,此時定義的所有區域和全域變數都會顯示在「執行和偵錯」檢視的「變數」視窗中。在我們的範例中,
item_name
設定為 'apple',quantity
設定為區域變數檢視下的 2,以及全域變數檢視下的空grocery_list
字典。現在讓我們使用 VS Code 的偵錯主控台進行一些探索。
-
選取
quantity <= 0
陳述式,在編輯器中按一下滑鼠右鍵,然後選取 在偵錯主控台中評估這會開啟偵錯主控台並執行選取的運算式。正如我們的範例中所預期的,運算式評估為
False
。偵錯主控台可能是快速測試運算式並更好地了解中斷點時程式碼狀態的強大工具。您也可以使用它來執行任意程式碼,例如呼叫函式或列印變數。您可以在 Python 教學課程 中了解更多關於 VS Code 中 Python 偵錯的資訊。
您現在可以透過選取「偵錯」檢視工具列中的 繼續,或按下 F5,繼續執行程式碼。
最後,讓我們為應用程式新增剩餘的路由,以便我們可以列出所有項目或特定項目,以及從我們的雜貨清單中移除它們。您可以讓偵錯工具保持執行狀態,因為當您儲存在下一步中所做的變更時,它會自動重新載入應用程式。
-
將
main.py
中的內容取代為以下程式碼from fastapi import FastAPI, HTTPException from models import ItemPayload app = FastAPI() grocery_list: dict[int, ItemPayload] = {} # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # if item already exists, we'll just add the quantity. # get all item names items_ids: dict[str, int] = { item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values() } if item_name in items_ids.keys(): # get index of item_name in item_ids, which is the item_id item_id: int = items_ids[item_name] grocery_list[item_id].quantity += quantity # otherwise, create a new item else: # generate an ID for the item based on the highest ID in the grocery_list item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0 grocery_list[item_id] = ItemPayload( item_id=item_id, item_name=item_name, quantity=quantity ) return {"item": grocery_list[item_id]} # Route to list a specific item by ID @app.get("/items/{item_id}") def list_item(item_id: int) -> dict[str, ItemPayload]: if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") return {"item": grocery_list[item_id]} # Route to list all items @app.get("/items") def list_items() -> dict[str, dict[int, ItemPayload]]: return {"items": grocery_list} # Route to delete a specific item by ID @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") del grocery_list[item_id] return {"result": "Item deleted."} # Route to remove some quantity of a specific item by ID @app.delete("/items/{item_id}/{quantity}") def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") # if quantity to be removed is higher or equal to item's quantity, delete the item if grocery_list[item_id].quantity <= quantity: del grocery_list[item_id] return {"result": "Item deleted."} else: grocery_list[item_id].quantity -= quantity return {"result": f"{quantity} items removed."}
-
儲存檔案 (⌘S (Windows、Linux Ctrl+S))。應用程式應自動重新載入。
您現在可以再次開啟 /docs
頁面並測試新的路由,使用偵錯工具和偵錯主控台來更好地了解程式碼執行。完成後,您可以停止偵錯工具 (⇧F5 (Windows、Linux Shift+F5))。您也可以透過按一下中斷點來移除我們在步驟 4 中新增的中斷點。
恭喜!您現在有一個可運作的 FastAPI 應用程式,其中包含新增、列出和刪除雜貨清單項目的路由。
設定資料儲存
此時,您已經擁有應用程式的可運作版本,其中包含基本功能。本節將引導您完成設定資料儲存以進行持久性的步驟,但如果您對已學到的知識感到滿意,您可以選擇跳過它。
到目前為止,我們將資料儲存在字典中,這並不理想,因為當應用程式重新啟動時,所有資料都會遺失。
為了持久保存資料,我們將使用 Redis,它是一個開放原始碼的記憶體內資料結構儲存區。由於其速度和多功能性,Redis 通常用作各種應用程式中的資料儲存系統,包括 Web 應用程式、即時分析系統、快取層、本教學課程等等。
如果您已經在 GitHub Codespaces 上使用我們現有的範本,您可以直接跳到 取代資料庫 區段。
如果您使用的是 Windows,您可以透過設定 Docker 容器 或 GitHub Codespace 來使用 Redis。在本教學課程中,我們將使用 Docker 容器,但您可以參考上面的 章節,以取得有關如何設定 GitHub Codespace 的指示。
否則,如果您使用的是 Linux 或 macOS 機器,您可以依照 他們網站上的指示 安裝 Redis,然後跳到 取代資料庫 區段。
在 Windows 上設定 Docker 容器
VS Code 開發容器 擴充功能提供了一種簡化的方法,可將您的專案、其相依性以及所有必要的工具整合到一個整潔的容器中,從而建立一個功能完整的開發環境。此擴充功能可讓您在 VS Code 中於容器內 (或掛載到容器中) 開啟您的專案,您將在其中擁有其完整的功能集。
對於以下步驟,請確保您的機器上已安裝以下需求
需求
- 適用於 Windows 的 Docker
- 開發容器 擴充功能
建立開發容器組態
-
開啟命令面板並執行 開發容器: 新增開發容器組態檔案…。
-
選取 Python 3
-
選取預設版本。
-
選取 Redis 伺服器 作為要安裝的其他功能,按一下 確定,然後選取 保留預設值。
我們可以選擇性地安裝 功能 以包含在容器中。在本教學課程中,我們將安裝 Redis 伺服器,這是一個社群貢獻的功能,可安裝 Redis 並新增適當的開發容器設定。
這會在您的工作區中建立一個
.devcontainer
資料夾,其中包含一個devcontainer.json
檔案。讓我們對此檔案進行一些編輯,以便容器設定包含安裝我們需要的 VS Code 擴充功能以及專案相依性等步驟。 -
開啟
devcontainer.json
檔案。 -
在
"features" : { ... }
項目後新增一個「,」,以便我們可以將更多設定新增至檔案。接下來,我們將必要的相依性安裝命令新增至
devcontainer.json
檔案中的postCreateCommand
屬性,以便我們的應用程式在容器設定完成後即可執行。 -
找到以下內容並移除該行的註解 (
//
),以便在建立容器後可以安裝相依性"postCreateCommand": "pip3 install --user -r requirements.txt",
您可以在 開發容器規格 中了解關於
postCreateCommand
和更多生命週期腳本的資訊。現在我們將使用
customizations
屬性來新增我們想要在容器中安裝的 VS Code 擴充功能。 -
將以下設定新增至
devcontainer.json
// Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "pip3 install --user -r requirements.txt", // Configure tool-specific properties. "customizations": { "vscode": { "extensions": [ "ms-python.python", //Python extension ID "ms-python.vscode-pylance" //Pylance extension ID ] } }
-
儲存檔案。
-
從右下角顯示的通知中選取 在容器中重新開啟,或從命令面板執行 開發容器: 在容器中重新開啟 命令。
注意:根據網際網路速度和機器效能,建置容器可能需要幾分鐘的時間。
您可以在 開發容器文件 中了解更多關於開發容器組態的資訊。
完成後,您將擁有一個完全設定的 Linux 基礎工作區,其中已安裝 Python 3 和 Redis 伺服器。
容器設定完成後,您會注意到 VS Code 左下角的一個指示器
注意:透過開啟「擴充功能」檢視 (⇧⌘X (Windows、Linux Ctrl+Shift+X)) 並搜尋它們,來仔細檢查 Python 和 Pylance 擴充功能是否已成功安裝在容器中。如果沒有,您可以透過執行 在開發容器中安裝 來安裝它們。
選取的 Python 解譯器資訊可在右下角的狀態列上找到,與 devcontainer.json
檔案中指定的版本相符
注意:如果您在狀態列上找不到 Python 解譯器資訊,您可以按一下 Python 解譯器指示器 (或從命令面板執行 Python: 選取解譯器 命令) 並手動選取容器中的 Python 解譯器。
我們現在準備好繼續進行下一節,我們將在其中取代資料儲存。
取代資料庫
我們有一個字典來儲存雜貨清單項目,但我們想要用 Redis 資料庫取代它。在本教學課程中,我們將使用 Redis 雜湊來儲存我們的資料,這是一種可以儲存多個鍵值對的資料結構。
與傳統資料庫不同,在傳統資料庫中,您可以在不知道項目 ID 的情況下擷取項目,您需要知道 Redis 雜湊鍵才能從中擷取值。在本教學課程中,我們將建立一個名為 item_name_to_id
的雜湊,以依名稱擷取項目,並將它們對應到其 ID。此外,我們還將建立其他雜湊來依 ID 擷取項目,將它們對應到其名稱和數量。每個項目雜湊都命名為 item_id:{item_id}
,並具有兩個欄位:item_name
和 quantity
。
首先,讓我們從用 Redis 用戶端物件取代字典開始,該物件連線到 Redis 伺服器。
-
在
main.py
檔案中,將檔案開頭的grocery_list: dict[int, ItemPayload] = {}
取代為以下幾行redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True)
Pylance 將顯示錯誤訊息,因為 Redis 尚未匯入。
-
將游標放在編輯器中的「redis」上,然後按一下顯示的燈泡 (或 ⌘. (Windows、Linux Ctrl+.))。然後選取 新增 'import redis'。
提示:您可以透過在設定編輯器 (⌘, (Windows、Linux Ctrl+,)) 中尋找 自動匯入完成 設定並啟用它,來設定 Pylance 自動新增匯入。
我們現在有一個 Redis 用戶端物件,它連線到在本機主機 (
host="0.0.0.0"
) 上執行並在連接埠 6379 (port=6379
) 上接聽的 Redis 伺服器。db
參數指定要使用的 Redis 資料庫。Redis 支援多個資料庫,在此程式碼中,我們將使用資料庫 0,這是預設資料庫。我們也傳遞decode_responses=True
以將回應解碼為字串 (而不是位元組)。讓我們在第一個路由
add_item
中進行更多取代。我們可以從 Redis 雜湊中直接擷取該資訊,而不是查看字典中的所有鍵來尋找已提供的項目名稱。我們假設
item_name_to_id
雜湊已存在,將項目名稱對應到其 ID (別擔心,我們很快就會新增此程式碼!)。然後,我們可以透過呼叫 Redis 的hget
方法來取得我們在請求中接收的項目名稱的 ID,如果請求的名稱已存在於雜湊中,則該方法將傳回項目 ID,如果不存在,則傳回None
。 -
刪除包含以下內容的行
items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()}
並將其取代為
item_id = redis_client.hget("item_name_to_id", item_name)
請注意,Pylance 會針對此變更引發問題。這是因為
hget
方法傳回str
或None
(如果項目不存在)。但是,我們尚未取代的程式碼下方的行預期item_id
的類型為int
。讓我們透過重新命名item_id
符號來解決此警告。 -
將
item_id
重新命名為item_id_str
。 -
如果您已啟用內嵌提示,Pylance 應在
item_id_str
旁邊顯示變數類型提示。您可以選擇性地按兩下以接受它 -
如果項目不存在,則
item_id_str
為None
。因此,我們現在可以刪除包含以下內容的行if item_name in items_ids.keys():
並將其取代為
if item_id_str is not None:
現在我們有了字串格式的項目 ID,我們需要將其轉換為
int
並更新項目的數量。目前,我們的 Redis 雜湊僅將項目名稱對應到其 ID。為了也將項目 ID 對應到其名稱和數量,我們將為每個項目建立一個單獨的 Redis 雜湊,使用"item_id:{item_id}"
作為我們的雜湊名稱,以便更輕鬆地依 ID 擷取。我們還將為每個雜湊新增item_name
和quantity
欄位。 -
刪除
if
區塊中的程式碼item_id: int = items_ids[item_name] grocery_list[item_id].quantity += quantity
並新增以下內容,以將
item_id
轉換為int
,然後透過呼叫 Redis 的hincrby
方法來遞增項目的數量。此方法會將"quantity"
欄位的值遞增請求中給定的數量 (quantity
)item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity)
我們現在只需要取代項目不存在時的程式碼,即當
item_id_str
為None
時。在這種情況下,我們會產生新的item_id
、為項目建立新的 Redis 雜湊,然後新增提供的項目名稱和數量。為了產生新的
item_id
,讓我們使用 Redis 的incr
方法,傳遞一個名為"item_ids"
的新雜湊。此雜湊用於儲存上次產生的 ID,因此我們可以在每次建立新項目時遞增它,確保它們都具有唯一的 ID。 -
刪除包含以下內容的行
item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0
並新增以下內容
item_id: int = redis_client.incr("item_ids")
當第一次使用
item_ids
鍵執行此incr
呼叫時,Redis 會建立該鍵並將其對應到值1
。然後,每次後續執行時,它都會將儲存的值遞增 1。現在我們將使用
hset
方法並提供欄位 (item_id
、item_name
和quantity
) 和值 (項目新建立的 ID 及其提供的名稱和數量) 的對應,將項目新增至 Redis 雜湊。 -
刪除包含以下內容的行
grocery_list[item_id] = ItemPayload( item_id=item_id, item_name=item_name, quantity=quantity )
並將其取代為以下內容
redis_client.hset( f"item_id:{item_id}", mapping={ "item_id": item_id, "item_name": item_name, "quantity": quantity, })
現在我們只需要將新建立的 ID 對應到項目名稱,方法是設定我們一開始參考的雜湊,
item_name_to_id
。 -
將這行程式碼加到路由的結尾,在
else
區塊內redis_client.hset("item_name_to_id", item_name, item_id)
-
刪除包含以下內容的行
return {"item": grocery_list[item_id]}
並將其取代為
return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)}
-
如果您願意,您可以嘗試對其他路由進行類似的替換。否則,您可以直接將檔案的整個內容替換為以下程式碼
import redis from fastapi import FastAPI, HTTPException from models import ItemPayload app = FastAPI() redis_client = redis.StrictRedis(host="0.0.0.0", port=6379, db=0, decode_responses=True) # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # Check if item already exists item_id_str: str | None = redis_client.hget("item_name_to_id", item_name) if item_id_str is not None: item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) else: # Generate an ID for the item item_id: int = redis_client.incr("item_ids") redis_client.hset( f"item_id:{item_id}", mapping={ "item_id": item_id, "item_name": item_name, "quantity": quantity, }, ) # Create a set so we can search by name too redis_client.hset("item_name_to_id", item_name, item_id) return { "item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) } # Route to list a specific item by ID but using Redis @app.get("/items/{item_id}") def list_item(item_id: int) -> dict[str, dict[str, str]]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") else: return {"item": redis_client.hgetall(f"item_id:{item_id}")} @app.get("/items") def list_items() -> dict[str, list[ItemPayload]]: items: list[ItemPayload] = [] stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id") for name, id_str in stored_items.items(): item_id: int = int(id_str) item_name_str: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") if item_name_str is not None: item_name: str = item_name_str else: continue # skip this item if it has no name item_quantity_str: str | None = redis_client.hget( f"item_id:{item_id}", "quantity" ) if item_quantity_str is not None: item_quantity: int = int(item_quantity_str) else: item_quantity = 0 items.append( ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity) ) return {"items": items} # Route to delete a specific item by ID but using Redis @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") else: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} # Route to remove some quantity of a specific item by ID but using Redis @app.delete("/items/{item_id}/{quantity}") def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity") # if quantity to be removed is higher or equal to item's quantity, delete the item if item_quantity is None: existing_quantity: int = 0 else: existing_quantity: int = int(item_quantity) if existing_quantity <= quantity: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) return {"result": f"{quantity} items removed."}
-
重新執行偵錯工具以測試此應用程式,方法是與
/docs
路由互動。完成後,您可以停止偵錯工具。
恭喜!您現在有一個可運作的 FastAPI 應用程式,其中包含新增、列出和刪除購物清單項目的路由,而且資料會持久儲存在 Redis 資料庫中。
選用:設定資料庫刪除
由於資料現在由 Redis 持久儲存,您可能會想要建立一個腳本來清除所有測試資料。若要這麼做,請建立一個名為 flushdb.py
的新檔案,並包含以下內容
import redis
redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True)
redis_client.flushdb()
然後,當您想要重設資料庫時,您可以在 VS Code 中開啟 flushdb.py
檔案,然後選取編輯器右上角的 [執行] 按鈕,或從命令選取區執行 [Python:在終端機中執行 Python 檔案] 命令。
請注意,這應該謹慎執行,因為它會刪除目前資料庫中的所有金鑰,如果在生產環境中執行,可能會導致資料遺失。
選用:建立 ChatGPT 外掛程式
透過 GitHub Codespaces,您可以在使用 ChatGPT 外掛程式 時託管您的應用程式以進行測試。ChatGPT 外掛程式是讓 ChatGPT 與現有 API 互動以增強 ChatGPT 功能的工具,使其能夠執行各種動作。ChatGPT 外掛程式目前尚未公開提供,但您可以加入他們的 等候名單 以取得存取權。一旦您取得存取權,您可以依照下方的直播錄影建立您自己的 ChatGPT 購物清單外掛程式
注意:所有個人 GitHub.com 帳戶的免費或 Pro 方案都包含每月免費使用 GitHub Codespaces 的配額。如需詳細資訊,請前往 關於 GitHub Codespaces 的計費方式。
後續步驟
感謝您跟著本教學課程!我們希望您學到一些關於 FastAPI 以及如何搭配 VS Code 使用的新知識。
本教學課程的完整程式碼專案可以在 GitHub 上找到:python-sample-vscode-fastapi-tutorial。
若要深入了解 FastAPI,請參閱官方文件。
若要在生產網站上試用應用程式,請查看教學課程 使用 Docker 容器將 Python 應用程式部署至 Azure App Service。
您也可以檢閱其他 VS Code Python 文章