Pydantic 與請求主體
POST、PUT、PATCH 通常把資料放在 JSON 請求主體(request body)。FastAPI 用 Pydantic 的 BaseModel 描述這份 JSON:你宣告欄位,框架負責解析、驗證、轉成 Python 物件。
宣告模型並接收 body
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(min_length=1, max_length=80)
price: float = Field(gt=0)
tags: list[str] = []
is_offer: bool | None = None
@app.post("/items/")
def create_item(item: Item):
return item
函式參數的型別是 BaseModel 子類時,FastAPI 會把它當成 JSON body。客戶端少傳 name、price 不是正數,或型別不對,都會得到 422 與欄位級錯誤,函式本體不會執行。
Field 用來加限制與文件說明,不必另外寫一堆 if。巢狀模型也支援:欄位型別可以是另一個 BaseModel。
路徑、查詢與 body 可以並存
FastAPI 依「參數從哪裡來」自動分類,不必自己標註大多數情況:
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item, q: str | None = None):
return {"item_id": item_id, "q": q, "item": item}
item_id在路徑裡 → 路徑參數item是 Pydantic 模型 → bodyq是簡單型別且不在路徑 → 查詢參數
單一 body 時這樣就夠。若要收兩個 JSON 物件,再用 Body 包起來,官方教學有完整例子。
為什麼這是 20% 的核心
同一份模型同時服務四件事:執行期驗證、編輯器補齊、OpenAPI 文件、以及你稍後要寫的回應模型。改欄位時只改一處,文件與錯誤訊息跟著變。
常見實務:
- 輸入與輸出用不同模型,例如
ItemCreate不含id,ItemPublic不含密碼雜湊。 - 可選欄位用
= None或預設值,不要把「沒傳」和「傳了 null」在業務上混成同一種意思卻不文件化。 - 需要從 ORM 物件轉出時,看 Pydantic 的
model_config/from_attributes(舊名orm_mode)。
from pydantic import BaseModel, ConfigDict
class ItemPublic(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
price: float