在現代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類型註解指定(如int、str),Pydantic會自動驗證輸入類型。
- 無默認值的字段(如id、name)是必填項,若不提供會報錯。
- 帶默認值的字段(如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/請求,請求體包含name和age,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')
五、常見問題與解決¶
-
Q:如何處理必填字段但允許空值?
A:用Field(..., ...)或Optional類型,並確保類型正確(如Optional[str]允許None但仍需類型匹配)。 -
Q:FastAPI返回模型時,如何避免暴露敏感字段?
A:用model_config的exclude參數排除字段:
class User(BaseModel):
name: str
password: str
model_config = ConfigDict(exclude_unset=False) # 默認不排除字段
# 或在返回時顯式指定需要返回的字段:return user.dict(exclude={"password"})
- Q:如何處理模型之間的複雜關係?
A:用嵌套模型、聯合類型(Union)或枚舉(Literal),避免過度嵌套導致可讀性下降。
總結¶
FastAPI+Pydantic的數據模型定義與序列化是現代Python API開發的黃金組合。通過本文,你已經掌握了:
- 基礎模型定義與驗證規則
- 序列化/反序列化的核心方法
- 字段別名、嵌套、繼承等最佳實踐
- 在FastAPI中如何結合模型處理請求與響應
接下來,你可以嘗試在自己的項目中應用這些知識,從簡單模型開始,逐步構建複雜業務邏輯。記住,Pydantic的強大之處在於“數據驗證”和“自動轉換”,合理使用它能讓你的API更健壯、開發更高效!