在 JSON 中创建自 pydantic.BaseModel exclude 如果未设置则可选

In JSON created from a pydantic.BaseModel exclude Optional if not set

我想排除所有在创建时未设置的可选值 JSON。在这个例子中:

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,而是因为它是可选的并且 z 没有关键字参数。在下面的两种情况下,我希望在 JSON.

中包含 z
Foo(x=1, z=None)
Foo(x=1, z=77)

在这个意义上如果有其他解决方案可以将z设置为可选,我很想看看。

您可以仅排除通过 已设置 未设置 None 的模型字段并集而未设置的可选模型字段=36=].

Pydantic 为导出方法 model.dict(...) 提供以下参数:

exclude_unset: whether fields which were not explicitly set when creating the model should be excluded from the returned dictionary; default False.

exclude_none: whether fields which are equal to None should be excluded from the returned dictionary; default 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}}