Pydantic 學習與使用
在 Fastapi 的 Web 開發中的數據驗證通常都是在使用 Pydantic 來進行數據的校驗,本文將對 Pydantic 的使用方法做記錄與學習。
**簡介:**Pydantic 是一個在 Python 中用于數據驗證和解析的第三方庫,它現在是 Python 使用最廣泛的數據驗證庫。它利用聲明式的方式定義數據模型和Python 類型提示的強大功能來執行數據驗證和序列化,使您的代碼更可靠、更可讀、更簡潔且更易于調試。它還可以從模型生成 JSON 架構,提供了自動生成文檔等功能,從而輕松與其他工具集成。
參考文章:
作者:暴走的海鴿
鏈接:https://juejin.cn/post/7395080141130039305
1. Pydantic 快速上手
# 安裝
pip install pydantic# 使用 email 驗證的時候需要額外安裝
pip install pydantic[email]
Pydantic 使用起來簡單直觀,需要最少的樣板代碼和配置。它適用于許多流行的 IDE 和靜態分析工具,例如 PyCharm、VS Code、mypy 等。Pydantic 可以輕松與其他流行的 Python 庫(如 Flask、Django、FastAPI 和 SQLAlchemy)集成,使其易于在現有項目中使用。
Pydantic 使用類型注解來定義模型的字段類型,以確保確保數據符合預期的類型和格式。你可以使用Python 內置的類型、自定義類型或者其他Pydantic 提供的驗證類型。
Pydantic 提供了從各種數據格式(例如 JSON、字典)到模型實例的轉換功能。它可以自動將輸入數據解析成模型實例,并保留類型安全性和驗證規則。
Pydantic 的核心驗證邏輯是用 Rust 編寫的,使其成為 Python 中最快的數據驗證庫之一。它還支持延遲驗證和緩存,以提高效率。
1.1 數據驗證
Pydantic 的核心驗證邏輯是用 Rust 編寫的,使其成為 Python 中最快的數據驗證庫之一。它還支持延遲驗證和緩存,以提高效率。然后,您可以使用類型注釋定義模型的屬性,并選擇性地提供默認值或驗證器。
from enum import Enum
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, conint, EmailStr, constrclass GenderEnum(str, Enum):"""性別類的枚舉, 使用多繼承創建;"""male = "男"female = "女"class User(BaseModel):"""繼承的基類;"""id: intname: strage: conint(ge=0, le=100) # 使用 Pydantic 限制數字范圍;gender: GenderEnumemail: EmailStr # 使用 Pydantic 的郵件約束來限制約束;signup_ts: Optional[datetime] = Nonepassword: constr(min_length=8, max_length=16)if __name__ == '__main__':user_data = {"id": 1,"name": "傻狗","age": 18,"gender": GenderEnum.male,"email": "123456788@qq.com","signup_ts": datetime.now(),"password": "<PASSWORD>",}user = User.model_validate(user_data)print(user)print(f"User id: {user.id}, User name: {user.name}, User email: {user.email}")"""
>>> 輸出:
id=1 name='傻狗' age=18 gender=<GenderEnum.male: '男'> email='123456788@qq.com' signup_ts=datetime.datetime(2025, 5, 24, 14, 25, 6, 493690) password='<PASSWORD>'
User id: 1, User name: 傻狗, User email: 123456788@qq.com
"""
上述的代碼就是針對字典中的數據完成數據的校驗;
當我們的校驗需求比較復雜的時候,我們就可以自己定義比較復雜的校驗函數進行使用
from enum import Enum
from datetime import datetime
from typing import Optional, List
from pydantic import BaseModel, conint, EmailStr, constr, field_validatorclass GenderEnum(str, Enum):"""性別類的枚舉, 使用多繼承創建;"""male = "男"female = "女"def check_name(name: str) -> str:""":param name::return:"""if not name.startswith("小"): # 檢查字符串是否以特定字符開頭raise ValueError("must be startswith 小")return nameclass User(BaseModel):"""繼承的基類;"""id: intname: strage: conint(ge=0, le=100) # 使用 Pydantic 限制數字范圍;# gender: GenderEnumemail: EmailStr # 使用 Pydantic 的郵件約束來限制約束;signup_ts: Optional[datetime] = None# password: constr(min_length=8, max_length=16)friends: List[str] = []validate_fields = field_validator("name")(check_name)@field_validator("age")@classmethod # 裝飾器的順序不要放錯, 否則可能不生效;def check_age(cls, age: int):if age < 18:raise ValueError("age must be >= 18")return ageif __name__ == '__main__':user_data = {"id": 123,"name": "小鹵蛋","age": 28, # 小于18 就會報錯;"email": "xiaoludan@example.com",'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],}user = User(**user_data)print(user.age) # 取出模型中的數據print(user.model_dump()) # 字典的形式輸出;
**特別注意:**Pydantic 是強制的校驗,不滿足的時候程序會進行報錯,注意和泛型進行區分;
上述的代碼,我們是一個字段的進行的校驗,那么我們能不能一次校驗多個字段呢?
from datetime import datetime
from typing import List, Optional
from typing_extensions import Self # 如果python版本不低于3.11,則可以直接從typing中導入Self
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, model_validatordef check_name(v: str) -> str:"""Validator to be used throughout"""if not v.startswith("小"):raise ValueError("must be startswith 小")return vclass User(BaseModel):id: intname: str = "小鹵蛋"age: intemail: EmailStrsignup_ts: Optional[datetime] = Nonefriends: List[str] = []validate_fields = field_validator("name")(check_name)@field_validator("age")@classmethoddef check_age(cls, age):if age < 18:raise ValueError("用戶年齡必須大于18歲")return age@model_validator(mode="after") # after 模式表示是所有字段完成驗證之后再進行額外的驗證;def check_age_and_name(self) -> Self:if self.age < 30 and self.name != "小鹵蛋":raise ValueError("用戶年齡必須小于30歲, 且名字必須為小鹵蛋")return selfif __name__ == '__main__':user_data = {"id": 123,"name": "小xi鹵蛋","age": 20,"email": "xiaoludan@example.com",'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],}try:user = User(**user_data)print(user.model_dump())except ValidationError as e:print(f"Validation error: {e.json()}")
上述的代碼會同時對age
和name
字段進行驗證,使用的是裝飾器model_validator
;
額外補充:上述的代碼中用到了Enum枚舉類,枚舉類是一種特殊的類,用于定義一組固定的常量。在編程中,我們經常需要表示一些有限的、固定的狀態或者選項。例如,一周的星期幾(星期一、星期二……星期日),或者一個交通信號燈的狀態(紅燈、黃燈、綠燈)。使用枚舉類可以清晰地表示這些固定的狀態,避免使用容易出錯的字符串或者整數來表示。
-
創建枚舉類
# Enum 是 Python 的標準庫 enum 模塊中的一個類,是創建枚舉類的基礎。 from enum import Enumclass Color(Enum):""" 枚舉類;"""RED = 1GREEN = 2BLUE = 3
-
枚舉類的基本屬性和方法
-
name 屬性,表示枚舉成員的名稱
print(Color.RED.name) # 輸出 "RED"
-
value
屬性:表示枚舉成員的值。print(Color.RED.value) # 輸出 1
-
__members__
屬性:返回一個包含枚舉類中所有成員的字典,鍵是成員名稱,值是成員本身。for name, member in Color.__members__.items():print(name, member)""" RED Color.RED GREEN Color.GREEN BLUE Color.BLUE """
-
__iter__
方法:可以對枚舉類進行迭代,迭代的是枚舉成員。for color in Color:print(color)""" Color.RED Color.GREEN Color.BLUE """
-
-
枚舉類的應用場景
在開發軟件時,很多對象都有狀態。例如,一個訂單的狀態可以是“已下單”“已支付”“已發貨”“已完成”等。使用枚舉類來表示這些狀態可以避免使用字符串或者整數來直接表示狀態,減少錯誤。當需要從一組固定的選項中選擇時,枚舉類也很有用。比如在一個圖形界面程序中,用戶可以選擇不同的字體樣式,這些字體樣式可以用枚舉類來表示。使用枚舉類可以使代碼更清晰。例如,用枚舉類表示星期幾,相比直接使用數字或者字符串,代碼更容易理解。
model_validator 是 Pydantic 中的一個裝飾器,用于在模型實例創建后進行驗證。它允許開發者在模型的所有字段都被解析和驗證之后,執行額外的驗證邏輯。這個裝飾器可以用于確保模型的多個字段之間的關系或其他復雜的驗證條件。
from typing import Annotatedfrom pydantic import BaseModel, Field, validate_callclass Person(BaseModel):name: str = Field(..., min_length=1, max_length=100)age: int = Field(..., gt=0, lt=20)# @validate_call
def greet(person: Person, message: Annotated[str, Field(min_length=1, max_length=100)]):print(f"Hello, {person.name}! {message}")# 正確的調用
greet(Person(name="公眾號", age=18), "How are you?")
greet(Person(name="公眾號", age=18), 1) # 不使用裝飾器會運行,但是與類型不符, 使用 @ 裝飾器后會報錯;
然而,我們通常是希望定義和使用要符合我們的預期,以避免不可預見的錯誤。
此時validate_call
裝飾器就可以很好的為我們實現這一需求。
1.2 計算屬性
核心的裝飾器: @computed_field # 計算屬性 @property 被裝飾函數以值的形式返回;
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, EmailStr, computed_fieldclass User(BaseModel):id: intname: str = "小鹵蛋"age: intemail: EmailStrsignup_ts: Optional[datetime] = Nonefriends: List[str] = []@computed_field # 計算屬性@propertydef link(self) -> str:return f"尼古拉斯 · {self.name}"if __name__ == '__main__':user_data = {"id": 123,"name": "小鹵蛋","age": 20,"email": "xiaoludan@example.com",'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],}#try:user = User(**user_data)print(f"{user.model_dump()} .... type: {type(user.model_dump())}")except ValidationError as e:print(f"Validation error: {e.json()}")
當使用裝飾link 屬性會跟隨著結果一起顯示出來,
2. Pydantic 配置管理
# 安裝依賴
pip install pydantic_settings
2.1 直接設置
pydantic_settings
可以很好的管理服務中的配置信息;使用的時候繼承 BaseSettings 可以直接從環境變量中讀取相對應的配置信息,也可以以手動填入的方式進行;
import os# 導入需要的配置;
from pydantic import HttpUrl, Field
from pydantic_settings import BaseSettings# 初始化環境變量
os.environ['DATABASE_HOST'] = "http://baidu.com"
os.environ['DATABASE_USER'] = "公眾號:海哥python"
os.environ['DATABASE_PASSWORD'] = "123456abcd"
os.environ['API_KEY'] = "DHKSDsdh*(sdds"class AppConfig(BaseSettings):"""應用配置類, 用來管理應用程序的配置信息;"""# 定義配置項# 定義 url 形式的配置;DATABASE_HOST: HttpUrl# 定義配置項database_user,類型為字符串,默認最小長度為5DATABASE_USER: str = Field(min_length=5)# 定義配置項database_password,類型為字符串,默認最小長度為10DATABASE_PASSWORD: str = Field(min_length=10)# 定義配置項api_key,類型為字符串,默認最小長度為8API_KEY: str = Field(min_length=8)# 打印配置類的實例化對象的模型信息,用于調試和確認配置的正確性
print(AppConfig().model_dump())
2.2 配置文件
在代碼的同級目錄下創建一個.env
的配置文件;
DATABASE_HOST=http://baidu.com
DATABASE_USER=公眾號:海哥python
DATABASE_PASSWORD=123456abcd
API_KEY=DHKSDsdh*(sdds
代碼變化不大, 只有加載的部分發生一些變化;
# 導入需要的配置;
from pydantic import HttpUrl, Field
from pydantic_settings import BaseSettings, SettingsConfigDictclass Settings(BaseSettings):"""定義模型的基本類;"""# 定義配置模型的設置,包括.env文件位置、編碼、大小寫敏感性和額外參數策略model_config = SettingsConfigDict(env_file=".env",env_file_encoding="utf-8",case_sensitive=False,extra="forbid")# 數據庫主機的URL,必須是一個有效的HTTP或HTTPS URLdatabase_host: HttpUrl# 數據庫用戶的名稱,最小長度為5個字符database_user: str = Field(min_length=5)# 數據庫用戶的密碼,最小長度為10個字符database_password: str = Field(min_length=10)# API的密鑰,最小長度為8個字符api_key: str = Field(min_length=8)if __name__ == '__main__':# 打印配置類的實例化對象的模型信息,用于調試和確認配置的正確性print(Settings().model_dump())
3. Pydantic 高級應用
3.1 數據嵌套
Pydantic 支持嵌套數的數據模型,方便管理復雜的數據結構。
from typing import List
from pydantic import BaseModel, conintclass Friend(BaseModel):name: strage: conint(gt=0, le=99)class User(BaseModel):name: strage: conint(gt=0, le=99)friends: List[Friend] # 設置字段嵌套 Friend 類.if __name__ == '__main__':# 創建并驗證數據user_data = {'name': '三體-章北海','age': 30,# 設置嵌套, 'friends': [{'name': '小鹵蛋', 'age': 3}, {'name': '李元芳', 'age': 18}]}user = User(**user_data)print(user)
3.2 Filed 的使用
Pydantic 的Field
函數是一個強大的工具,它允許你在模型字段上設置額外的驗證規則和默認值。Field
函數通常與模型字段一起使用,以提供更多的定制選項。
參數 | 具體含義 |
---|---|
... | 表示該字段是必填項 |
default | 用于定義字段的默認值 |
default_factory | 用于定義字段的默認值函數 |
alias | 字段定義別名 |
validation_alias | 字段定義別名,只想將別名用于驗證 |
serialization_alias | 字段定義別名,只想定義用于序列化的別名 |
gt 、lt 、ge 等 | 約束數值,大于、小于、大于或等于 等 |
min_length 、max_length 等 | 約束字符串 |
min_items 、max_items 等 | 元組、列表或集合約束 |
validate_default | 控制是否應驗證字段的默認值,默認情況下,不驗證字段的默認值。 |
strict | 指定是否應在“嚴格模式”下驗證字段 |
frozen | 用于模擬凍結的數據類行為 |
exclude | 用于控制導出模型時應從模型中排除哪些字段 |
pattern | 對于字符串字段,您可以設置為 pattern 正則表達式以匹配該字段所需的任何模式。 |
示例代碼:
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, Field, EmailStr, ValidationError, SecretStrclass User(BaseModel):id: int = Field(..., alias="_id", frozen=True, strict=True)name: str = Field(default="小鹵蛋", min_length=1, max_length=100) # 設置默認值,使用 min_length 和 max_length 來限制字符串長度age: int = Field(gt=0) # 支持各類條件驗證,這里假設年齡必須大于0email: EmailStrsignup_ts: Optional[datetime] = Field(default_factory=datetime.now, nullable=False, validate_default=True)friends: List[str] = Field(default=[], min_items=0)passwd: SecretStr = Field(min_length=6, max_length=20, exclude=True) # passwd不會被序列化if __name__ == '__main__':print(User.model_json_schema())user_data = {"_id": 123, # 使用別名 _id"name": "小鹵蛋","age": 20,"email": "xiaoludan@example.com",# 'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],"passwd": "123456"}try:user = User(**user_data)print(f"創建用戶: {user}")print(f"轉成字典形式: {user.model_dump()} .... type: {type(user.model_dump())}")print(f"轉成json格式:{user.model_dump_json()} .... type: {type(user.model_dump_json())}")print(f"用戶屬性: User id: {user.id}, User name: {user.name}, User email: {user.email}")# user.id = 456 # 這里修改會報錯except ValidationError as e:print(f"Validation error: {e.json()}")
3.3 Config 配置
如果要對BaseModel中的某一基本型進行統一的格式要求,我們還可以使用Config類來實現。以下是一些 Config 類中常見的屬性及其含義:
參數 | 取值類型 | 具體含義 |
---|---|---|
str_min_length | int | str 類型的最小長度,默認值為None |
str_max_length | int | str 類型的最大長度。默認值為None |
extra | str | 在模型初始化期間是否忽略、允許或禁止額外的屬性。默認值為 ‘ignore’。allow - 允許任何額外的屬性。forbid - 禁止任何額外的屬性。ignore - 忽略任何額外的屬性。 |
frozen | bool | 模型是否可變 |
str_to_upper | bool | 是否將 str 類型的所有字符轉換為大寫。默認值為 False 。 |
str_strip_whitespace | bool | 是否去除 str 類型的前導和尾隨空格。 |
str_to_lower | bool | 是否將 str 類型的所有字符轉換為小寫。默認值為 False 。 |
#! -*-conding: UTF-8 -*-from pydantic import BaseModelclass User(BaseModel):name: strage: intclass Config:str_min_length = 10 # 字符串最小長度str_max_length = 20 # 字符串最大長度user = User(name="John Doe", age=30) # 此時字段不滿足約束會報錯;
3.4 數據序列化
使用模型類
.model_dump()
方法可以將一個模型類型實例對象轉換為字典類型的數據。
#! -*-conding: UTF-8 -*-
# @公眾號: 海哥python
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, field_serializer
from enum import Enumclass GenderEnum(str, Enum):"""性別枚舉"""male = "男"female = "女"class User(BaseModel):id: intname: str = "小鹵蛋"age: intemail: EmailStrsignup_ts: Optional[datetime] = datetime.now()friends: List[str] = []sex: GenderEnum@field_validator("age") # 裝飾器的順序不能變化;@classmethoddef check_age(cls, age):if age < 18:raise ValueError("用戶年齡必須大于18歲")return age@field_serializer('signup_ts', when_used="always")def serialize_signup_ts(self, value: datetime) -> str:"""默認情況下, datetime 對象被序列化為 ISO 8601 字符串。這里使用field_serializer自定義序列化規則。"""return value.strftime('%Y-%m-%d %H:%M:%S')@field_serializer('sex', when_used="always")def serialize_sex(self, value) -> str:return value.valueif __name__ == '__main__':user_data = {"id": 123,"name": "小鹵蛋","age": 20,"email": "xiaoludan@example.com",# 'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],"sex": "男",}try:user = User.model_validate(user_data)print(f"{user.model_dump()} .... type: {type(user.model_dump())}")except ValidationError as e:print(f"Validation error: {e.json()}")
使用模型類的
.model_dump_json()
方法可以將一個模型的實例轉換成為 JSON 字符串。
from datetime import datetime
from typing import List, Optional, Any
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, field_serializer, model_serializerclass User(BaseModel):id: intname: str = "小鹵蛋"age: intemail: EmailStrsignup_ts: Optional[datetime] = datetime.now()friends: List[str] = []@field_validator("age")@classmethoddef check_age(cls, age):if age < 18:raise ValueError("用戶年齡必須大于18歲")return age@field_serializer('signup_ts', when_used="json")def serialize_signup_ts(self, value: datetime) -> str:return value.strftime('%Y-%m-%d %H:%M:%S')@model_serializer(when_used="json")def serialize_model(self) -> dict[str, Any]:return {'id': self.id,'name': self.name,'age': self.age + 1,'email': self.email,'signup_ts': self.serialize_signup_ts(self.signup_ts),'friends': self.friends,}if __name__ == '__main__':user_data = {"id": 123,"name": "小鹵蛋","age": 20,"email": "xiaoludan@example.com",# 'signup_ts': '2024-07-19 00:22','friends': ["公眾號:海哥python", '小天才', b''],}try:user = User.model_validate(user_data)print(f"{user.model_dump_json()} .... type: {type(user.model_dump_json())}")print(f"{user.model_dump()} .... type: {type(user.model_dump())}")except ValidationError as e:print(f"Validation error: {e.json()}")
也可以使用model_serializer
對整體模型的序列化做定制。結果如下:
"""
{"id":123,"name":"小鹵蛋","age":21,"email":"xiaoludan@example.com","signup_ts":"2024-07-24 14:17:42","friends":["公眾號:海哥python","小天才",""]} .... type: <class 'str'>
{'id': 123, 'name': '小鹵蛋', 'age': 20, 'email': 'xiaoludan@example.com', 'signup_ts': datetime.datetime(2024, 7, 24, 14, 17, 42, 45474), 'friends': ['公眾號:海哥python', '小天才', '']} .... type: <class 'dict'>
"""
3.5 文檔生成
Pydantic 可以自動生成 API 文檔。
#! -*-conding: UTF-8 -*-from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, EmailStr, field_validatorclass User(BaseModel):id: intname: str = "小鹵蛋"age: intemail: EmailStrsignup_ts: Optional[datetime] = Nonefriends: List[str] = []@field_validator("age")def check_age(cls, age):if age < 18:raise ValueError("用戶年齡必須大于18歲")return ageif __name__ == '__main__':print(User.model_json_schema())
通過model_json_schema
方法可以得到API文檔。
4. Fastapi 中的 Pydantic
在 FastAPI 中,與普通的程序相同,可以通過定義 Pydantic 模型來指定請求參數的結構和類型。以下是一個簡單的例子:
""" 定義參數模型;
"""from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = None
在 Fastapi 中,你可以將 Pydantic 模型作為函數參數,Fastapi 會自動解析請求中的 json 數據,并將其轉換成為 Pydantic 模型實例。如果數據不符合模型定義,FastApi 會自動返回一個 422 錯誤。
import uvicorn
from fastapi import FastAPI, Request, Form
from fastapi.params import Depends
from pydantic import BaseModel
from watchfiles import awatchapp = FastAPI(title="測試小Demo",description="文檔描述",version="1.0",
)class QueryParam(BaseModel):q: str | None = Nonelimit: int = 10offset: int = 0def get_query_params(params: QueryParam = Depends()):print(params)return params@app.get("/query", tags=["query"])
async def query(params: QueryParam = Depends(get_query_params)):return {"params": params}@app.get("/filter", tags=["filter"])
async def my_filter(params: QueryParam):print(params)return paramsif __name__ == '__main__':uvicorn.run(app, host="127.0.0.1", port=8000)
上述返回的就是 422 錯誤表明,沒有通過校驗。
上述的函數并沒有指定通過那種方式指定解析數據,如果不顯式指定 Body
或 Query
,FastAPI 會根據參數的位置和類型自動推斷參數的來源:
- 如果參數是函數的路徑參數(即在路徑中定義的參數),則會從路徑中提取。
- 如果參數是函數的普通參數(非路徑參數),且沒有顯式指定
Body
或Query
,則會根據參數的類型來推斷:- 如果參數是復雜類型(如 Pydantic 模型),則會從請求體中提取。
- 如果參數是簡單類型(如
str
、int
等),則會從查詢字符串中提取。
4.1 設置校驗規則
from pydantic import BaseModel, Fieldclass Item(BaseModel):name: str = Field(..., min_length=3)description: str = Field(None, max_length=100)price: float = Field(..., gt=0, le=100)tax: float = Field(None, ge=0, le=100)"""
min_length=3:name 的長度至少為 3。
max_length=100:description 的長度最多為 100。
gt=0:price 必須大于 0。
le=100:price 必須小于或等于 100。
ge=0:tax 必須大于或等于 0。
le=100:tax 必須小于或等于 100。
"""
4.2 自定義校驗方法
可以使用@validator
裝飾器來定義自定義校驗方法。
from pydantic import BaseModel, validatorclass Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = None@validator("price")def check_price(cls, v):if v < 0:raise ValueError("price must be greater than 0")return v
4.3 路徑參數和查詢參數
Pydantic 模型不僅可以用于請求體,還可以用于查詢參數和路徑參數。例如:
from fastapi import FastAPI, Query
from pydantic import BaseModelapp = FastAPI()class QueryParams(BaseModel):query: str = Query(..., min_length=3, max_length=50)@app.get("/items/")
async def get_items(query_params: QueryParams):return {"query": query_params.query}
在這個例子中,query_params
是一個 QueryParams
類型的 Pydantic 模型,它定義了一個查詢參數 query
。FastAPI 會自動解析查詢參數,并將其轉換為 QueryParams
實例。
4.4 響應模型
可以使用 Pydantic 模型來定義響應數據的結構。例如:
import uvicorn
from fastapi import FastAPI, Request, Form
from fastapi.params import Depends
from pydantic import BaseModel
from watchfiles import awatchapp = FastAPI(title="測試小Demo",description="文檔描述",version="1.0",
)class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = None@app.post("/items/", response_model=Item)
async def create_item(item: Item):return itemif __name__ == '__main__':uvicorn.run(app, host="127.0.0.1", port=8000)
在這個例子中,response_model=Item
表示響應數據的結構將遵循 Item
模型的定義。FastAPI 會自動將返回的數據轉換為 Item
實例,并確保其符合模型定義。
4.5 結合 Depends
Pydantic
和 Depends
可以結合使用,以實現更強大的參數校驗和依賴注入功能。
-
模型校驗
Depends
主要用于聲明依賴,例如獲取數據庫連接、驗證用戶身份等。而Pydantic
模型則用于數據驗證和轉換。你可以將Pydantic
模型作為依賴函數的返回值,這樣就可以在路由函數中直接使用經過校驗的數據。# !/usr/bin/env python # -*-coding:utf-8 -*-""" # File : app.py # Time : 2025/5/27 15:49 # Author : 紫青寶劍 """ import uvicorn from fastapi import FastAPI, Request, Form, HTTPException from fastapi.params import Depends from pydantic import BaseModel from watchfiles import awatchapp = FastAPI(title="測試小Demo",description="文檔描述",version="1.0", )class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Nonedef val_item(item: Item):print("接收到了執行的邏輯....")if item.tax < 0.0:raise HTTPException(status_code=400, detail="Price must be greater than zero")else:return item@app.post("/items/", response_model=Item) async def create_item(item: Item = Depends(val_item)):return itemif __name__ == '__main__':uvicorn.run(app, host="127.0.0.1", port=8000)
此時,會直接在模型校驗數據的類型之后執行視圖函數之前,執行的校驗步驟。
-
參數查詢
from fastapi import FastAPI, Depends, Query from pydantic import BaseModelapp = FastAPI()class QueryParameters(BaseModel):q: str = Query(..., min_length=3, max_length=50)@app.get("/items/") async def read_items(query_params: QueryParameters = Depends()):return {"q": query_params.q}
QueryParameters
是一個 Pydantic 模型,它定義了一個查詢參數q
。通過將query_params
作為依賴項,FastAPI 會自動解析查詢參數并將其轉換為QueryParameters
實例。 -
前置校驗
在路由函數中使用
Depends
,創建一個前置依賴函數來執行復雜的驗證邏輯。from fastapi import FastAPI, Depends, HTTPExceptionapp = FastAPI()def complex_query_validator(query: str):if "forbidden" in query:raise HTTPException(status_code=400, detail="Forbidden query")return query@app.get("/items/") async def read_items(query: str = Depends(complex_query_validator)):return {"query": query}
complex_query_validator
是一個依賴函數,它接收一個查詢參數query
并對其進行校驗。如果校驗失敗,它會拋出一個HTTPException
。 -
用戶認證
Depends
也可以用于用戶認證和授權。你可以定義一個依賴函數來驗證用戶身份,并返回用戶信息。然后在路由函數中使用這個依賴函數。from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials from pydantic import BaseModelapp = FastAPI() security = HTTPBasic()class User(BaseModel):""" 定義用戶模型的類。"""username: strpassword: strdef get_current_user(credentials: HTTPBasicCredentials = Depends(security)) -> User:""" 設置校驗的函數;"""valid_users = {"Alice": "alice_passwd"}if credentials.username not in valid_users or valid_users[credentials.username] != credentials.password:raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)return User(username=credentials.username, password=credentials.password)@app.post("/users/") async def read_users(user: User = Depends(get_current_user)):return user
在這個例子中,
get_current_user
是一個依賴函數,它接收 HTTP 基本認證的憑據,并驗證用戶身份。如果驗證成功,它會返回一個User
實例。 -
總結
Pydantic
和Depends
的結合使用可以讓你在 FastAPI 中實現強大的參數校驗和依賴注入功能。你可以通過定義 Pydantic 模型來校驗請求數據,同時使用 Depends 來聲明依賴項,從而實現復雜的業務邏輯和數據校驗。這種組合不僅提高了代碼的可維護性和可讀性,還增強了 API 的安全性。
繼續努力,終成大器!