各位观众老爷们,大家好! 今天咱们来聊聊 FastAPI 和 Pydantic 这对黄金搭档,看看它们是如何强强联合,在数据验证和类型强制方面搞事情的。
开场白:数据界的“照妖镜”和“整形医生”
在Web API 的世界里,数据就像是进城的农民工,质量参差不齐。你辛辛苦苦写了一个 API,结果前端传来的数据不是缺胳膊就是少腿,要么就是类型不对。这可咋办? 难道要我们自己手动写一堆 if-else
来验证? 那得写到猴年马月啊!
这时候,Pydantic 就闪亮登场了。它可以说是数据界的“照妖镜”和“整形医生”。 “照妖镜”是指它能帮你识别出数据里妖魔鬼怪,确保数据的结构和类型符合你的预期;“整形医生”是指它能帮你把数据转换成你想要的类型,让它们看起来更顺眼。 而 FastAPI 则把 Pydantic 集成得非常完美,让你用起来就像呼吸一样自然。
Pydantic 基础:定义数据模型
Pydantic 的核心是数据模型(Data Model)。 我们可以通过定义一个继承自 pydantic.BaseModel
的类来创建一个数据模型。这个类里的每个属性都代表着数据的一个字段,并且可以指定该字段的类型和验证规则。
举个栗子:
from pydantic import BaseModel, validator
from typing import Optional
class User(BaseModel):
id: int
name: str
signup_ts: Optional[datetime] = None
friends: List[int] = []
@validator('name')
def name_must_contain_space(cls, v):
if ' ' not in v:
raise ValueError('must contain a space')
return v
在这个例子中,我们定义了一个 User
模型,它有 id
、name
、signup_ts
和 friends
四个字段。
id
是一个整数 (int
),必填。name
是一个字符串 (str
),必填。signup_ts
是一个可选的日期时间 (Optional[datetime]
),可以为None
。friends
是一个整数列表 (List[int]
),默认为空列表。
注意 @validator('name')
这个装饰器。它定义了一个自定义的验证器,用于检查 name
字段是否包含空格。如果 name
字段不包含空格,就会抛出一个 ValueError
异常。
Pydantic 的类型注解:让数据无所遁形
Pydantic 依赖于 Python 的类型注解(Type Hints)来确定数据字段的类型。 类型注解可以帮助 Pydantic 在运行时进行类型检查和转换。
常用的类型注解包括:
类型 | 描述 | 例子 |
---|---|---|
int |
整数 | id: int |
float |
浮点数 | price: float |
str |
字符串 | name: str |
bool |
布尔值 | is_active: bool |
datetime |
日期时间 | created_at: datetime |
date |
日期 | birthday: date |
time |
时间 | start_time: time |
list[T] |
元素类型为 T 的列表 |
tags: List[str] |
dict[K, V] |
键类型为 K ,值类型为 V 的字典 |
metadata: Dict[str, Any] |
Optional[T] |
T 类型或 None |
description: Optional[str] = None |
Union[T1, T2, ...] |
T1 、T2 等类型中的一个 |
status: Union[str, int] |
FastAPI 与 Pydantic 的完美结合:API 的数据卫士
FastAPI 能够自动使用 Pydantic 模型来验证请求和响应的数据。 你只需要在 API 函数的参数或返回值中声明 Pydantic 模型, FastAPI 就会自动完成数据验证和类型转换。
from fastapi import FastAPI, HTTPException
from datetime import datetime
from typing import List, Optional
app = FastAPI()
@app.post("/users/")
async def create_user(user: User):
# 在这里你可以安全地使用 user 对象,因为 FastAPI 已经验证过它的数据
print(user.name)
return user
在这个例子中, create_user
函数的参数 user
被声明为 User
类型。 当客户端发送一个 POST 请求到 /users/
路径时, FastAPI 会自动使用 User
模型来验证请求体中的数据。
- 如果数据验证成功, FastAPI 会将请求体中的数据转换成一个
User
对象,并传递给create_user
函数。 - 如果数据验证失败, FastAPI 会自动返回一个 HTTP 422 错误,其中包含详细的错误信息。
Pydantic 的验证器:自定义你的验证规则
Pydantic 提供了多种验证器,可以让你自定义数据验证的规则。
- 字段验证器(Field Validators): 使用
@validator
装饰器,可以验证单个字段的值。 - 模型验证器(Model Validators): 使用
@root_validator
装饰器,可以验证整个模型的值。
from pydantic import BaseModel, validator, root_validator
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
@validator('price')
def price_must_be_positive(cls, v):
if v <= 0:
raise ValueError('price must be positive')
return v
@root_validator
def tax_must_be_less_than_price(cls, values):
price = values.get('price')
tax = values.get('tax')
if tax is not None and price is not None and tax >= price:
raise ValueError('tax must be less than price')
return values
在这个例子中,我们定义了两个验证器:
price_must_be_positive
是一个字段验证器,用于检查price
字段的值是否为正数。tax_must_be_less_than_price
是一个模型验证器,用于检查tax
字段的值是否小于price
字段的值。
Pydantic 的类型转换:让数据更听话
Pydantic 不仅可以验证数据,还可以将数据转换成你想要的类型。 例如,你可以将一个字符串转换成一个整数,或者将一个日期字符串转换成一个 datetime
对象。
from pydantic import BaseModel, validator
from datetime import datetime
class Event(BaseModel):
start_time: datetime
@validator('start_time', pre=True)
def parse_start_time(cls, v):
if isinstance(v, str):
try:
return datetime.fromisoformat(v)
except ValueError:
raise ValueError('invalid datetime format')
return v
在这个例子中,我们定义了一个 parse_start_time
验证器,用于将 start_time
字段的值转换成一个 datetime
对象。 pre=True
表示这个验证器会在其他验证器之前运行。
Pydantic 的配置:定制你的数据模型
Pydantic 允许你通过 Config
类来配置数据模型的行为。 例如,你可以指定数据模型的别名、是否忽略大小写、是否允许额外的字段等等。
from pydantic import BaseModel, Field
class Product(BaseModel):
product_id: int = Field(alias="productId")
name: str
price: float
class Config:
allow_population_by_field_name = True
extra = 'forbid'
在这个例子中,我们配置了 Product
模型的以下行为:
allow_population_by_field_name = True
表示允许使用字段名来填充数据模型。 这意味着你可以使用product_id
或productId
来设置product_id
字段的值。extra = 'forbid'
表示禁止在数据模型中出现额外的字段。 如果客户端发送的请求体中包含额外的字段, Pydantic 会抛出一个错误。
高级用法:嵌套模型和泛型
Pydantic 还支持嵌套模型和泛型,让你能够处理更复杂的数据结构。
- 嵌套模型: 你可以在一个数据模型中嵌套其他的 Pydantic 模型。
- 泛型: 你可以使用泛型来定义可以处理不同类型的数据模型。
from pydantic import BaseModel
from typing import List, Generic, TypeVar
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str
price: float
images: List[Image]
T = TypeVar('T')
class Response(BaseModel, Generic[T]):
data: T
message: str = "Success"
# 使用
item_data = {
"name": "Awesome Product",
"description": "A very cool product",
"price": 99.99,
"images": [
{"url": "http://example.com/image1.jpg", "name": "Image 1"},
{"url": "http://example.com/image2.jpg", "name": "Image 2"}
]
}
item = Item(**item_data)
response_data = Response[Item](data=item)
print(response_data.json(indent=4))
在这个例子中, Item
模型嵌套了 Image
模型, Response
模型使用了泛型来表示不同类型的数据。
总结:Pydantic + FastAPI = 数据安全感
总而言之, Pydantic 和 FastAPI 的结合,就像是给你的 API 穿上了一层坚不可摧的盔甲。 它们能够自动验证和转换数据,让你不再为数据质量而烦恼。 这样你就可以把更多的时间和精力放在业务逻辑上,而不是浪费在无聊的数据验证上。
记住, 好的 API 就像一辆好车,不仅要跑得快,还要足够安全可靠。 而 Pydantic 和 FastAPI 就是你打造安全可靠 API 的最佳搭档。
好了,今天的讲座就到这里。 感谢各位的收听! 咱们下次再见!