问题描述
我想排除所有在创建 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
没有关键字参数。在下面的两种情况下,我希望在 JSON 中有 z
。
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,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,z=77))))
{"a": 4,"b": 52,"d": {"x": 3,"y": 42}}
{"a": 4,"y": 42,"z": null},"c": null}
{"a": 4,"c": 78,"z": 77}}