跳轉至

建立 2026-10-11 更新 2026-10-11

FastAPI 整合

FastAPI 是建立在 Pydantic 之上的 Python Web 框架:在路由函式的參數寫上 Pydantic 模型,FastAPI 就會自動解析請求內容、驗證資料、在失敗時回傳 HTTP 422,並把模型寫進 OpenAPI 文件(/docs)。本頁說明請求模型、回應模型、建立/更新/輸出模型分離的寫法,以及 PATCH 部分更新。

怎麼用 Pydantic 模型接收請求內容?

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class ItemCreate(BaseModel):
    name: str = Field(min_length=1, max_length=50)
    price: float = Field(gt=0)
    tags: list[str] = []


@app.post("/items")
def create_item(item: ItemCreate):
    # 進到這裡時,item 已經是驗證過的 ItemCreate 物件
    return {"name": item.name, "price_with_tax": round(item.price * 1.05)}

用 uv run fastapi dev main.py 啟動後,打開 http://127.0.0.1:8000/docs 就能看到依模型自動產生的互動式文件。送出不合法的資料(例如 price: -1)時,FastAPI 會自動回傳 422,內容就是 Pydantic 的錯誤清單:

{
  "detail": [
    {
      "type": "greater_than",
      "loc": ["body", "price"],
      "msg": "Input should be greater than 0",
      "input": -1,
      "ctx": {"gt": 0}
    }
  ]
}

為什麼要另外定義回應模型?

response_model 決定 API 回傳什麼欄位。它會過濾掉模型沒有宣告的欄位,避免把密碼雜湊之類的內部資料不小心傳出去:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserCreate(BaseModel):
    email: EmailStr
    password: str


class UserOut(BaseModel):
    id: int
    email: EmailStr


@app.post("/users", response_model=UserOut, status_code=201)
def create_user(user: UserCreate):
    record = {"id": 1, "email": user.email, "password_hash": "hashed..."}
    return record  # password_hash 會被 UserOut 過濾掉

建立、更新、輸出要用同一個模型嗎?

不建議。三種情境需要的欄位不同,常見做法是用繼承拆成多個模型:

模型 用途 特點
UserBase 共用欄位 被其他模型繼承
UserCreate 建立時的請求 包含密碼,id 由伺服器產生所以沒有
UserUpdate 更新時的請求 所有欄位都是選填
UserOut 回應 包含 id,不包含密碼
from pydantic import BaseModel, EmailStr


class UserBase(BaseModel):
    email: EmailStr
    full_name: str | None = None


class UserCreate(UserBase):
    password: str


class UserUpdate(BaseModel):
    email: EmailStr | None = None
    full_name: str | None = None


class UserOut(UserBase):
    id: int

PATCH 部分更新怎麼只改有傳的欄位?

用 model_dump(exclude_unset=True) 取得「使用者真的有傳」的欄位,再套用到既有資料:

from pydantic import BaseModel


class UserUpdate(BaseModel):
    email: str | None = None
    full_name: str | None = None


stored = {"email": "old@example.com", "full_name": "Alice"}
patch = UserUpdate.model_validate({"full_name": "Alice Chen"})

stored.update(patch.model_dump(exclude_unset=True))
print(stored)  # {'email': 'old@example.com', 'full_name': 'Alice Chen'}

如果改用 model_dump(),沒傳的 email 會變成 None,把原本的資料覆蓋掉。

推薦影音

Corey Schafer:FastAPI 教學第 4 集:Pydantic Schemas

簡述:在 FastAPI 專案中建立請求與回應模型、處理驗證錯誤,示範本頁的模型分離做法,是 Corey Schafer FastAPI 系列的一集。

BugBytes:FastAPI 與 Pydantic 模型、巢狀模型

簡述:示範在 FastAPI 中使用模型類別與巢狀模型,適合搭配本頁與 巢狀模型 一起看。

相關資料