我尝试从 API 接受数据,然后使用 Pydantic 基本模型验证响应结构。但是,我遇到的情况是,有时某些字段不会包含在响应中,而有时会包含在响应中。问题是,当我尝试验证结构时,Pydantic 开始抱怨这些字段“丢失”,尽管它们有时可能会丢失。我真的不明白如何将一个字段定义为“missible”。文档提到,仅定义为名称和类型的字段被认为是这种方式,但我没有任何运气
这是我想要实现的目标的一个简单示例
# Response: {a: 1, b: "abc", c: ["a", "b", "c"]}
response: dict = json.loads(request_response)
# Pydantic Base Model
from pydantic import BaseModel
class Model(BaseModel):
a: int
b: str
c: List[str]
d: float
# Validating
Model(**response)
# Return: ValidationError - Missing "d" field
如何使“d”不会导致验证抛出错误?我尝试将“d”切换为
d: Optional[float]
和d: Optional[float] = 0.0
,但没有任何效果。
谢谢!
模型要么有字段,要么没有。从某种意义上说,字段“总是”需要在完全初始化的模型实例上具有值。只是如果在初始化期间没有显式提供值,则字段可能会有一个将分配给它的“default”值。 (请参阅文档中的基本模型用法)
您面临的问题最终是:如果未明确设置,应为字段 d
分配什么值?它应该是
None
还是应该是某个默认的
float
值(例如 0.
)还是其他值?无论您选择什么,都必须在模型定义中指定默认值,并记住使用正确的类型注释字段。如果您选择默认的 float
(例如 0.
),您的类型保持不变,只需将字段定义为
d: float = 0.
。如果您希望默认值是不同类型,例如 None
,则需要将定义更改为 d: float | None = None
。为了完整起见,您还可以定义一个默认工厂而不是静态值,以便在初始化期间计算实际值。
这是一个简短的演示:
from pydantic import BaseModel, Field, ValidationError
class Model(BaseModel):
a: int
b: str
c: list[str]
d: float | None = None # equivalent: `d: typing.Optional[float] = None`
e: float = 0.
f: float = Field(default_factory=lambda: 420.69)
if __name__ == '__main__':
instance = Model.model_validate({
"a": 1,
"b": "abc",
"c": ["a", "b", "c"],
})
print(instance.model_dump_json(indent=4))
try:
Model.model_validate({
"a": 1,
"b": "abc",
"c": ["a", "b", "c"],
"d": None, # fine
"e": None, # error
"f": None, # error
})
except ValidationError as e:
print(e.json(indent=4))
输出:
{
"a": 1,
"b": "abc",
"c": [
"a",
"b",
"c"
],
"d": null,
"e": 0.0,
"f": 420.69
}
[
{
"type": "float_type",
"loc": [
"e"
],
"msg": "Input should be a valid number",
"input": null,
"url": "https://errors.pydantic.dev/2.7/v/float_type"
},
{
"type": "float_type",
"loc": [
"f"
],
"msg": "Input should be a valid number",
"input": null,
"url": "https://errors.pydantic.dev/2.7/v/float_type"
}
]
typing.Optional
对于 Pydantic 场的行为记录很少。也许是因为它被认为是显而易见的。我个人认为这并不明显,因为
Optional[T]
相当于
Union[T, None]
(或新符号中的 T | None
)。使用任何其他类型的联合注释字段,同时省略默认值将导致该字段为必填字段。但如果您使用包含 None
的并集进行注释,则该字段会自动接收 None
默认值。有点不一致,但就是这样。
但是,问题最终是您希望模型字段是什么。如果未明确设置,应为字段d
分配什么值?它应该是 None
还是应该是某个默认的
float
值? (例如0.
)如果None
可以,那么您可以使用Optional[float]
或float | None
,并且不需要指定默认值。 (指定 Optional[float] = None
是等效的。)如果您想要任何其他默认值,则需要相应地指定它,例如d: float = 0.0
;但在这种情况下,None
将是该字段的无效值。
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
a: int
b: str
c: list[str]
d: float | None # or typing.Optional[float]
e: float = 0.
if __name__ == '__main__':
print(Model.parse_obj({
"a": 1,
"b": "abc",
"c": ["a", "b", "c"],
}), "\n")
try:
Model.parse_obj({
"a": 1,
"b": "abc",
"c": ["a", "b", "c"],
"e": None,
})
except ValidationError as e:
print(e)
输出:
a=1 b='abc' c=['a', 'b', 'c'] d=无 e=0.0 模型有 1 个验证错误 e none 不是允许的值 (type=type_error.none.not_allowed)