FastAPI+Pydantic:數據模型定義與序列化最佳實踐

在現代Web開發中,數據模型的定義和處理是核心環節之一。FastAPI作爲高性能Python API框架,搭配Pydantic這個強大的數據驗證與序列化庫,能讓數據處理變得既簡單又可靠。本文將從零開始,用通俗易懂的語言和實用的例子,帶你掌握FastAPI+Pydantic的數據模型定義與序列化最佳實踐。

爲什麼選FastAPI+Pydantic?

  • FastAPI:高性能、自動生成API文檔(Swagger UI)、支持異步,且對數據驗證有原生支持。
  • Pydantic:專爲數據驗證設計,能自動檢查輸入數據類型、格式,並將其轉換爲Python對象;同時支持序列化和反序列化。

兩者結合後,你只需定義一個數據模型,FastAPI就能自動幫你處理請求驗證、數據轉換和響應格式化,大幅減少重複代碼。

一、快速上手:定義第一個Pydantic模型

Pydantic的核心是BaseModel類,所有數據模型都繼承自它。我們先從最簡單的模型開始:

from pydantic import BaseModel

# 定義一個用戶信息模型
class User(BaseModel):
    id: int          # 整數類型,必填(無默認值則必須提供)
    name: str        # 字符串類型,必填
    age: int = 18    # 整數類型,有默認值18(可選填)
    email: str | None = None  # Python 3.10+ 可選類型,默認None

# 使用模型
user1 = User(id=1, name="Alice", age=25)  # 不提供email,使用默認值None
user2 = User(id=2, name="Bob", email="bob@example.com")  # 不提供age,使用默認值18

關鍵點
- 字段類型由Python類型註解指定(如intstr),Pydantic會自動驗證輸入類型。
- 無默認值的字段(如idname)是必填項,若不提供會報錯。
- 帶默認值的字段(如age=18)是可選的,未提供時自動使用默認值。
- 可選類型用| None(Python 3.10+)或Optional[int](兼容舊版本),表示該字段可以爲None

二、數據驗證:Pydantic的“安全網”

Pydantic最強大的功能是自動驗證數據。當輸入數據不符合模型定義時,它會拋出詳細的錯誤信息,幫你快速定位問題。

1. 基礎驗證:類型與格式檢查

# 錯誤示例:age爲字符串類型,與模型定義的int不匹配
try:
    invalid_user = User(id=3, name="Charlie", age="twenty", email="charlie@example.com")
except Exception as e:
    print(e)  # 輸出錯誤:"twenty" is not a valid integer

2. 高級驗證:自定義約束條件

通過Pydantic的Field類,可以給字段添加更細粒度的驗證規則(需導入Field):

from pydantic import Field

class User(BaseModel):
    name: str = Field(..., min_length=2, max_length=50)  # 字符串長度2-50
    age: int = Field(18, ge=18, le=120)  # age必須≥18且≤120
    email: str | None = Field(None, regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")  # 郵箱格式正則

# 驗證示例
valid_user = User(name="David", age=20, email="david@example.com")
invalid_email = User(name="Eve", age=25, email="invalid-email")  # 郵箱格式錯誤

常用約束條件
- min_length/max_length:字符串長度限制
- ge(Greater Than or Equal):大於等於(如ge=18
- le(Less Than or Equal):小於等於(如le=100
- gt/lt:大於/小於
- regex:字符串格式正則匹配
- const:固定值(如const="admin"

三、序列化與反序列化:模型與數據的雙向轉換

Pydantic模型能輕鬆實現Python對象與字典、JSON的互相轉換,這在API請求/響應處理中非常關鍵。

1. 模型→字典/JSON

user = User(id=1, name="Alice", age=25, email="alice@example.com")

# 轉爲字典(字段名+值)
user_dict = user.dict()
print(user_dict)  # {'id': 1, 'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}

# 轉爲JSON字符串
user_json = user.json()
print(user_json)  # {"id": 1, "name": "Alice", "age": 25, "email": "alice@example.com"}

2. 字典/JSON→模型

Pydantic可以直接從字典創建模型實例,自動完成類型轉換和驗證:

data = {"id": 2, "name": "Bob", "age": 30}
user = User(**data)  # 相當於 User(id=2, name="Bob", age=30)

3. FastAPI中的實際應用

在FastAPI中,Pydantic模型可直接作爲請求體(POST/PUT)和響應體(GET/POST返回數據):

from fastapi import FastAPI

app = FastAPI()

# 定義請求體模型
class CreateUser(BaseModel):
    name: str = Field(..., min_length=2)
    age: int = Field(18, ge=18)

# 定義響應模型
class UserResponse(BaseModel):
    id: int
    name: str
    age: int

# 模擬數據庫存儲
fake_db = {1: User(id=1, name="Alice", age=25)}

@app.post("/users/", response_model=UserResponse)
def create_user(user: CreateUser):
    # 假設新用戶ID爲len(fake_db)+1
    new_id = len(fake_db) + 1
    new_user = User(id=new_id, name=user.name, age=user.age)
    fake_db[new_id] = new_user
    return new_user  # FastAPI自動序列化爲JSON響應

效果:當你發送POST /users/請求,請求體包含nameage,FastAPI會自動驗證並轉換爲CreateUser模型,處理後返回UserResponse模型,同時生成Swagger文檔。

四、最佳實踐:讓模型更實用的技巧

1. 字段別名:統一命名風格

當JSON字段名與Python變量名不一致時(如JSON用user_id,Python用userId),用alias解決:

class User(BaseModel):
    user_id: int = Field(..., alias="user_id")  # 前端JSON中的鍵名是user_id
    name: str

# 解析JSON時,"user_id"對應Python變量user_id
json_data = {"user_id": 1, "name": "Alice"}
user = User(**json_data)
print(user.user_id)  # 1,Python變量名與JSON鍵名通過alias映射

2. 嵌套模型:複雜結構複用

如果模型包含另一個模型(如用戶信息包含地址),直接嵌套定義即可:

class Address(BaseModel):
    street: str
    city: str

class User(BaseModel):
    name: str
    address: Address  # 嵌套Address模型

# 使用時,直接傳入嵌套字典
user_data = {
    "name": "Bob",
    "address": {"street": "123 Main St", "city": "Beijing"}
}
user = User(**user_data)
print(user.address.city)  # "Beijing"

3. 模型繼承:代碼複用

當多個模型有共同字段時,用繼承減少重複代碼:

class BaseModel(BaseModel):
    id: int
    created_at: datetime = Field(default_factory=datetime.utcnow)  # 當前時間

class User(BaseModel):
    name: str

class Admin(BaseModel):
    is_admin: bool

class SuperUser(User, Admin):  # 繼承User和Admin
    pass  # 繼承後包含User的name、Admin的is_admin、BaseModel的id和created_at?
    # 注意:Pydantic模型繼承需確保父模型無衝突字段,且使用單繼承更穩妥

4. 忽略額外字段:處理未知數據

當輸入數據包含模型中沒有的字段時,默認會報錯。若允許忽略額外字段,用extra="ignore"

class User(BaseModel):
    name: str
    model_config = ConfigDict(extra="ignore")  # 忽略額外字段,不報錯

# 即使JSON包含"gender": "male",模型也會忽略
user_data = {"name": "Charlie", "gender": "male"}
user = User(**user_data)
print(user)  # User(name='Charlie')

五、常見問題與解決

  1. Q:如何處理必填字段但允許空值?
    A:用Field(..., ...)Optional類型,並確保類型正確(如Optional[str]允許None但仍需類型匹配)。

  2. Q:FastAPI返回模型時,如何避免暴露敏感字段?
    A:用model_configexclude參數排除字段:

   class User(BaseModel):
       name: str
       password: str
       model_config = ConfigDict(exclude_unset=False)  # 默認不排除字段
       # 或在返回時顯式指定需要返回的字段:return user.dict(exclude={"password"})
  1. Q:如何處理模型之間的複雜關係?
    A:用嵌套模型、聯合類型(Union)或枚舉(Literal),避免過度嵌套導致可讀性下降。

總結

FastAPI+Pydantic的數據模型定義與序列化是現代Python API開發的黃金組合。通過本文,你已經掌握了:
- 基礎模型定義與驗證規則
- 序列化/反序列化的核心方法
- 字段別名、嵌套、繼承等最佳實踐
- 在FastAPI中如何結合模型處理請求與響應

接下來,你可以嘗試在自己的項目中應用這些知識,從簡單模型開始,逐步構建複雜業務邏輯。記住,Pydantic的強大之處在於“數據驗證”和“自動轉換”,合理使用它能讓你的API更健壯、開發更高效!

小夜