在从pydantic.BaseModel创建的JSON中,如果未设置,则排除可选
我想排除所有在创建 JSON 时未设置的 Optional 值。在这个例子中:
from pydantic import BaseModel
from typing import Optional
class Foo(BaseModel):
x: int
y: int = 42
z: Optional[int]
print(Foo(x=3).json())
我明白了{"x": 3, "y": 42, "z": null}。但我想排除z. 不是因为它的值是None,而是因为它是 Optional 并且没有z. 在下面的两种情况下,我想z在 JSON 中使用。
Foo(x=1, z=None)
Foo(x=1, z=77)
如果有任何其他解决方案z可以在这个意义上设置为可选,我希望看到它。
回答
你可以排除未设置通过使该模型领域的联盟唯一可选的模型字段设置和那些不无。
Pydantic 为导出方法model.dict(...)提供了以下参数:
exclude_unset: 创建模型时未明确设置的字段是否应从返回的字典中排除;默认False。
exclude_none: 是否None应该从返回的字典中排除等于的字段;默认False
要合并两个字典,我们可以使用表达式a = {**b, **c}(来自 的c值覆盖来自 的值b)。请注意,从 Python 3.9 开始,它可以像a = b | c.
from pydantic import BaseModel
from typing import Optional
from pydantic.json import pydantic_encoder
import json
class Foo(BaseModel):
x: int
y: int = 42
z: Optional[int]
def exclude_optional_dict(model: BaseModel):
return {**model.dict(exclude_unset=True), **model.dict(exclude_none=True)}
def exclude_optional_json(model: BaseModel):
return json.dumps(exclude_optional_dict(model), default=pydantic_encoder)
print(exclude_optional_json(Foo(x=3))) # {"x": 3, "y": 42}
print(exclude_optional_json(Foo(x=3, z=None))) # {"x": 3, "z": null, "y": 42}
print(exclude_optional_json(Foo(x=3, z=77))) # {"x": 3, "z": 77, "y": 42}
更新
为了使用嵌套模型的方法,我们需要对两个字典进行深度联合(或合并),如下所示:
def union(source, destination):
for key, value in source.items():
if isinstance(value, dict):
node = destination.setdefault(key, {})
union(value, node)
else:
destination[key] = value
return destination
def exclude_optional_dict(model: BaseModel):
return union(model.dict(exclude_unset=True), model.dict(exclude_none=True))
class Foo(BaseModel):
x: int
y: int = 42
z: Optional[int]
class Bar(BaseModel):
a: int
b: int = 52
c: Optional[int]
d: Foo
print(exclude_optional_json(Bar(a=4, d=Foo(x=3))))
print(exclude_optional_json(Bar(a=4, c=None, d=Foo(x=3, z=None))))
print(exclude_optional_json(Bar(a=4, c=78, d=Foo(x=3, z=77))))
{"a": 4, "b": 52, "d": {"x": 3, "y": 42}}
{"a": 4, "b": 52, "d": {"x": 3, "y": 42, "z": null}, "c": null}
{"a": 4, "b": 52, "c": 78, "d": {"x": 3, "y": 42, "z": 77}}
THE END
二维码