根据字段值使用不同的 Pydantic 模型
- 2025-04-10 09:45:00
- admin 原创
- 24
问题描述:
我有 2 个 Pydantic 模型(var1
和var2
)。方法的输入PostExample
可以接收第一个模型或第二个模型的数据。使用Union
有助于解决此问题,但在验证过程中,它会为第一个和第二个模型引发错误。
如何使得在填写字段时出现错误时,仅针对某个模型返回验证器错误,而不是同时针对两者返回?(如果有帮助,可以通过字段 A 的长度来区分模型)。
主程序
@app.post("/PostExample")
def postExample(request: Union[schemas.var1, schemas.var2]):
result = post_registration_request.requsest_response()
return result
schemas.py
class var1(BaseModel):
A: str
B: int
C: str
D: str
class var2(BaseModel):
A: str
E: int
F: str
解决方案 1:
您可以使用可区分联合(感谢@larsks在评论部分提到这一点)。设置可区分联合,“验证速度更快,因为它只针对一个模型进行尝试”,并且“在失败的情况下只会引发一个显式错误”。下面给出了工作示例。
另一种方法是尝试解析模型(基于您作为查询/路径参数传递的鉴别器),如此答案(选项 1)中所述。
工作示例
应用程序
import schemas
from fastapi import FastAPI, Body
from typing import Union
app = FastAPI()
@app.post("/")
def submit(item: Union[schemas.Model1, schemas.Model2] = Body(..., discriminator='model_type')):
return item
schemas.py
from typing import Literal
from pydantic import BaseModel
class Model1(BaseModel):
model_type: Literal['m1']
A: str
B: int
C: str
D: str
class Model2(BaseModel):
model_type: Literal['m2']
A: str
E: int
F: str
测试输入 - 输出
#1 Successful Response #2 Validation error #3 Validation error
# Request body # Request body # Request body
{ { {
"model_type": "m1", "model_type": "m1", "model_type": "m2",
"A": "string", "A": "string", "A": "string",
"B": 0, "C": "string", "C": "string",
"C": "string", "D": "string" "D": "string"
"D": "string" } }
}
# Server response # Server response # Server response
200 { {
"detail": [ "detail": [
{ {
"loc": [ "loc": [
"body", "body",
"Model1", "Model2",
"B" "E"
], ],
"msg": "field required", "msg": "field required",
"type": "value_error.missing" "type": "value_error.missing"
} },
] {
} "loc": [
"body",
"Model2",
"F"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
解决方案 2:
对于那些寻求纯 pydantic 解决方案(不使用 FastAPI)的人来说:
您需要:
构建一个附加模型(从技术上讲,是一个中间注释)来“收集和执行”可区分的联合,
解析使用
parse_obj_as()
该方法如下所示:
感谢@Chris之前的回答,该解决方案即基于此回答。
from typing import Literal, Union, Annotated
from pydantic import BaseModel, Field, parse_obj_as
class Model1(BaseModel):
model_type: Literal['m1']
A: str
B: int
C: str
D: str
class Model2(BaseModel):
model_type: Literal['m2']
A: str
E: int
F: str
# Create a new model to represent the discriminated union
ValidModel = Annotated[Union[Model1, Model2], Field(discriminator='model_type')]
# Sample data
raw_data = {
"model_type": "m1",
"A": "foo",
"B": 1,
"C": "bar",
"D": "zap"
}
# Parse as the correct model based on `model_type`
my_model = parse_obj_as(ValidModel, raw_data)
print(type(my_model)) # <class '__main__.Model1'>
解决方案 3:
Pydantic 更新
Pydantic 已弃用parse_obj_as
并替换为TypeAdapter
。我修改了@yaakov-bressler 的精彩答案。现在它的速度也快了很多,我猜想这是由于 Pydantic 的改进。
# %%
import json
from typing import Annotated, Literal, Union
from pydantic import BaseModel, Field, TypeAdapter, parse_obj_as
# %%
class Model1(BaseModel):
key: Literal["Model1", "Model1A"]
value: int
class Model2(BaseModel):
key: Literal["Model2", "Model2A"]
value2: int
name: str
# %%
ValidatorModel = Annotated[Union[Model1, Model2], Field(discriminator="key")]
# %%
adaptor = TypeAdapter(ValidatorModel)
# %% JSON Examples
model1 = {"key": "Model1", "value": 1}
model2 = {"key": "Model2", "value2": 2, "name": "name"}
model1a = {"key": "Model1A", "value": 23}
# %% Parse JSON New Way
%%timeit
for model in [model1, model2, model1a]:
x = adaptor.validate_python(model)
# 2.06 µs ± 25.1 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# %% Deprecated way
%%timeit
for model in [model1, model2, model1a]:
x = parse_obj_as(ValidatorModel, model)
# 669 µs ± 43.1 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD