是的,有.这就是一般的generics和特别的generic models的目的.
例如,您创建了一个type variable M
,并将其上限设置为BaseModel
,然后定义一个由该类型变量参数化的GenericModel
类,并用List[M]
注释其data
字段
示例:
from typing import Any, Dict, Generic, List, Optional, TypeVar
from pydantic import BaseModel, Field
from pydantic.generics import GenericModel
M = TypeVar("M", bound=BaseModel)
class ApiResponse(GenericModel, Generic[M]):
success: bool
data: List[M] = Field(default_factory=list)
message: Optional[str] = None
meta: Dict[str, Any] = Field(default_factory=dict)
class User(BaseModel):
name: str
favorite_sandwich: str
class Point2D(BaseModel):
x: float
y: float
然后,您可以这样定义您的路由,例如:
from fastapi import FastAPI
# ... import ApiResponse, Point2D, User
app = FastAPI()
@app.get("/users", response_model=ApiResponse[User])
async def get_groups() -> Dict[str, Any]:
users = [
User(name="Alice", favorite_sandwich="BLT"),
User(name="Bob", favorite_sandwich="Ham&Cheese"),
]
count = len(users)
return {
"success": True,
"data": users,
"meta": {"count": count},
}
@app.get("/points")
async def get_points() -> ApiResponse[Point2D]:
points = [Point2D(x=3.14, y=0), Point2D(x=-100.1, y=420.69)]
return ApiResponse(success=True, data=points)
向这些端点发送GET
个请求会产生以下响应正文:
{
"success": true,
"data": [
{
"name": "Alice",
"favorite_sandwich": "BLT"
},
{
"name": "Bob",
"favorite_sandwich": "Ham&Cheese"
}
],
"message": null,
"meta": {
"count": 2
}
}
{
"success": true,
"data": [
{
"x": 3.14,
"y": 0
},
{
"x": -100.1,
"y": 420.69
}
],
"message": null,
"meta": null
}
如果判断为这些端点生成的JSON架构,您将看到类型参数被正确解析,并且这data
个列表元素架构对于这两个端点是不同的.
Side notes:
最初的ApiResponse
模型将data
和meta
都定义为Optional
,但您的缺省值对应于"实际"字段类型(即分别为list
和dict
).Optional[T]
相当于Union[T, None]
.因此,除非您希望允许None
作为这些字段的值,否则我建议go 掉那里的Optional
.设置默认值已使这些字段不再是必填项.(请参阅我的示例)
此外,在处理像list
或dict
这样的可变类型时,使用default_factory
模式而不是赋值缺省值是probably的好主意(或者至少是好的形式).
当您的路由处理程序函数的返回类型实际上与您想要的响应模型匹配时(例如,在我的points
路由中),您可以省略路由修饰符的response_model
参数,而只用它注释函数本身.当它doesn't匹配时(如在users
中),您仍然应该正确地注释返回类型(在该示例Dict[str, Any]
中),但在修饰符中提供正确的response_model
.(详情见here)