Skip to content

建立 2026-09-14 更新 2026-09-14

Pydantic 與請求主體

POSTPUTPATCH 通常把資料放在 JSON 請求主體(request body)。FastAPI 用 PydanticBaseModel 描述這份 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。客戶端少傳 nameprice 不是正數,或型別不對,都會得到 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 模型 → body
  • q 是簡單型別且不在路徑 → 查詢參數

單一 body 時這樣就夠。若要收兩個 JSON 物件,再用 Body 包起來,官方教學有完整例子。

為什麼這是 20% 的核心

同一份模型同時服務四件事:執行期驗證、編輯器補齊、OpenAPI 文件、以及你稍後要寫的回應模型。改欄位時只改一處,文件與錯誤訊息跟著變。

常見實務:

  • 輸入與輸出用不同模型,例如 ItemCreate 不含 idItemPublic 不含密碼雜湊。
  • 可選欄位用 = 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

進一步學習