跳到内容

序列化

除了直接通过字段名称(例如 model.foobar)访问模型属性之外,模型还可以通过多种方式进行转换、转储、序列化和导出。

序列化与转储

Pydantic 交替使用术语“序列化”和“转储”。两者都指将模型转换为字典或 JSON 编码字符串的过程。

在 Pydantic 之外,“序列化”一词通常指将内存中的数据转换为字符串或字节。但是,在 Pydantic 的上下文中,将对象从更结构化的形式(例如 Pydantic 模型、数据类等)转换为由 Python 内置类型(如 dict)组成的结构化程度较低的形式之间存在非常密切的关系。

虽然我们可以(并且偶尔会)通过在转换为原始类型时使用“转储”一词,在转换为字符串时使用“序列化”来区分这些情况,但出于实际目的,我们经常使用“序列化”一词来指代这两种情况,即使它并不总是意味着转换为字符串或字节。

model.model_dump(...)

API 文档

pydantic.main.BaseModel.model_dump

这是将模型转换为字典的主要方法。子模型将被递归转换为字典。

默认情况下,输出可能包含非 JSON 可序列化的 Python 对象。可以将 mode 参数指定为 'json',以确保输出仅包含 JSON 可序列化类型。还存在其他参数来包含或排除字段,包括嵌套字段,或进一步自定义序列化行为。

有关更多信息,请参阅可用的 参数

注意

子模型被转换为字典的一个例外是,RootModel 及其子类将直接转储 root 字段值,而没有包装字典。这也是递归完成的。

注意

您可以使用 计算字段propertycached_property 数据包含在 model.model_dump(...) 输出中。

示例

from typing import Any, Optional

from pydantic import BaseModel, Field, Json


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: Optional[float] = 1.1
    foo: str = Field(serialization_alias='foo_alias')
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_unset=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_none=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}


class Model(BaseModel):
    x: list[Json[Any]]


print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}
from typing import Any

from pydantic import BaseModel, Field, Json


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float | None = 1.1
    foo: str = Field(serialization_alias='foo_alias')
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_unset=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_none=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}


class Model(BaseModel):
    x: list[Json[Any]]


print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}

model.model_dump_json(...)

API 文档

pydantic.main.BaseModel.model_dump_json

.model_dump_json() 方法直接将模型序列化为 JSON 编码的字符串,该字符串等效于 .model_dump() 生成的结果。

有关更多信息,请参阅可用的 参数

注意

Pydantic 可以将许多常用的类型序列化为 JSON,否则这些类型与简单的 json.dumps(foobar)(例如 datetimedateUUID)不兼容。

from datetime import datetime

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    foo: datetime
    bar: BarModel


m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123})
print(m.model_dump_json())
#> {"foo":"2032-06-01T12:13:14","bar":{"whatever":123}}
print(m.model_dump_json(indent=2))
"""
{
  "foo": "2032-06-01T12:13:14",
  "bar": {
    "whatever": 123
  }
}
"""

dict(model) 和迭代

Pydantic 模型也可以使用 dict(model) 转换为字典,您还可以使用 for field_name, field_value in model: 迭代模型的字段。使用这种方法,将返回原始字段值,因此子模型不会转换为字典。

示例

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(dict(m))
#> {'banana': 3.14, 'foo': 'hello', 'bar': BarModel(whatever=123)}
for name, value in m:
    print(f'{name}: {value}')
    #> banana: 3.14
    #> foo: hello
    #> bar: whatever=123

另请注意,RootModel 确实 会转换为以键 'root' 为键的字典。

自定义序列化器

Pydantic 提供了几个 函数式序列化器 来自定义模型如何序列化为字典或 JSON。

可以使用 @field_serializer 装饰器在字段上自定义序列化,并可以使用 @model_serializer 装饰器在模型上自定义序列化。

from datetime import datetime, timedelta, timezone
from typing import Any

from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer


class WithCustomEncoders(BaseModel):
    model_config = ConfigDict(ser_json_timedelta='iso8601')

    dt: datetime
    diff: timedelta

    @field_serializer('dt')
    def serialize_dt(self, dt: datetime, _info):
        return dt.timestamp()


m = WithCustomEncoders(
    dt=datetime(2032, 6, 1, tzinfo=timezone.utc), diff=timedelta(hours=100)
)
print(m.model_dump_json())
#> {"dt":1969660800.0,"diff":"P4DT4H"}


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> dict[str, Any]:
        return {'x': f'serialized {self.x}'}


print(Model(x='test value').model_dump_json())
#> {"x":"serialized test value"}

注意

通过将特殊值 '*' 传递给 @field_serializer 装饰器,也可以在所有字段上调用单个序列化器。

此外,PlainSerializerWrapSerializer 使您可以使用函数来修改序列化的输出。

这两个序列化器都接受可选参数,包括

  • return_type 指定函数的返回类型。如果省略,将从类型注解中推断出来。
  • when_used 指定何时应使用此序列化器。接受一个字符串,值包括“always”、“unless-none”、“json”和“json-unless-none”。默认为“always”。

PlainSerializer 使用简单的函数来修改序列化的输出。

from typing import Annotated

from pydantic import BaseModel
from pydantic.functional_serializers import PlainSerializer

FancyInt = Annotated[
    int, PlainSerializer(lambda x: f'{x:,}', return_type=str, when_used='json')
]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,234'}

WrapSerializer 接收原始输入以及应用标准序列化逻辑的处理函数,并且可以在将结果值作为序列化的最终输出返回之前对其进行修改。

from typing import Annotated, Any

from pydantic import BaseModel, SerializerFunctionWrapHandler
from pydantic.functional_serializers import WrapSerializer


def ser_wrap(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
    return f'{nxt(v + 1):,}'


FancyInt = Annotated[int, WrapSerializer(ser_wrap, when_used='json')]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,235'}

覆盖转储模型时的返回类型

虽然 .model_dump() 的返回值通常可以描述为 dict[str, Any],但通过使用 @model_serializer,您实际上可以使其返回与此签名不匹配的值

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x


print(Model(x='not a dict').model_dump())
#> not a dict

如果您想这样做并且仍然为此方法获得正确的类型检查,则可以在 if TYPE_CHECKING: 块中覆盖 .model_dump()

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Literal

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x

    if TYPE_CHECKING:
        # Ensure type checkers see the correct return type
        def model_dump(
            self,
            *,
            mode: Literal['json', 'python'] | str = 'python',
            include: Any = None,
            exclude: Any = None,
            by_alias: bool | None = False,
            exclude_unset: bool = False,
            exclude_defaults: bool = False,
            exclude_none: bool = False,
            round_trip: bool = False,
            warnings: bool = True,
        ) -> str: ...

此技巧实际上在 RootModel 中用于此目的。

序列化子类

标准类型的子类

标准类型的子类会自动像其超类一样转储

from datetime import date, timedelta
from typing import Any

from pydantic_core import core_schema

from pydantic import BaseModel, GetCoreSchemaHandler


class DayThisYear(date):
    """
    Contrived example of a special type of date that
    takes an int and interprets it as a day in the current year
    """

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        return core_schema.no_info_after_validator_function(
            cls.validate,
            core_schema.int_schema(),
            serialization=core_schema.format_ser_schema('%Y-%m-%d'),
        )

    @classmethod
    def validate(cls, v: int):
        return date(2023, 1, 1) + timedelta(days=v)


class FooModel(BaseModel):
    date: DayThisYear


m = FooModel(date=300)
print(m.model_dump_json())
#> {"date":"2023-10-28"}

BaseModel、dataclass、TypedDict 字段的子类实例

当使用其注解本身是结构化类型(例如,BaseModel 子类、数据类等)的字段时,默认行为是将属性值序列化,就好像它是注解类型的实例一样,即使它是子类。更具体地说,只有来自注解类型的字段才会包含在转储的对象中

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(name='pydantic', password='hunter2')

m = OuterModel(user=user)
print(m)
#> user=UserLogin(name='pydantic', password='hunter2')
print(m.model_dump())  # note: the password field is not included
#> {'user': {'name': 'pydantic'}}

迁移警告

此行为与 Pydantic V1 中的工作方式不同,在 Pydantic V1 中,我们在递归将模型转储到字典时始终包含所有(子类)字段。此行为更改背后的动机是,它有助于确保您准确知道序列化时可能包含哪些字段,即使在实例化对象时传递了子类也是如此。特别是,这可以帮助防止在将密码等敏感信息添加为子类的字段时出现意外。

使用鸭子类型 🦆 进行序列化

什么是使用鸭子类型进行序列化?

鸭子类型序列化是根据对象本身中存在的字段而不是对象模式中存在的字段来序列化对象的行为。这意味着,当序列化对象时,子类中存在但在原始模式中不存在的字段将包含在序列化输出中。

此行为是 Pydantic V1 中的默认行为,但在 V2 中已更改,以帮助确保您准确知道序列化时将包含哪些字段,即使在实例化对象时传递了子类也是如此。这有助于防止在序列化包含敏感信息的子类时出现安全风险,例如。

如果您想要 v1 风格的鸭子类型序列化行为,则可以使用运行时设置,或注解各个类型。

  • 字段/类型级别:使用 SerializeAsAny 注解
  • 运行时级别:在调用 model_dump()model_dump_json() 时使用 serialize_as_any 标志

我们在下面更详细地讨论这些选项

SerializeAsAny 注解:

如果您想要鸭子类型序列化行为,可以使用类型上的 SerializeAsAny 注解来完成

from pydantic import BaseModel, SerializeAsAny


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    as_any: SerializeAsAny[User]
    as_user: User


user = UserLogin(name='pydantic', password='password')

print(OuterModel(as_any=user, as_user=user).model_dump())
"""
{
    'as_any': {'name': 'pydantic', 'password': 'password'},
    'as_user': {'name': 'pydantic'},
}
"""

当字段被注解为 SerializeAsAny[<SomeType>] 时,验证行为将与注解为 <SomeType> 时相同,并且像 mypy 这样的类型检查器也会将属性视为具有适当的类型。但是,在序列化时,该字段将被序列化,就好像该字段的类型提示是 Any 一样,这就是名称的由来。

serialize_as_any 运行时设置

serialize_as_any 运行时设置可用于使用或不使用鸭子类型序列化行为来序列化模型数据。可以将 serialize_as_any 作为关键字参数传递给 BaseModelRootModelmodel_dump()model_dump_json 方法。也可以将其作为关键字参数传递给 TypeAdapterdump_python()dump_json() 方法。

如果 serialize_as_any 设置为 True,则模型将使用鸭子类型序列化行为进行序列化,这意味着模型将忽略模式,而是询问对象本身应如何序列化。特别是,这意味着当序列化模型子类时,将包含子类中存在但原始模式中不存在的字段。

如果 serialize_as_any 设置为 False(这是默认值),则模型将使用模式进行序列化,这意味着子类中存在但原始模式中不存在的字段将被忽略。

为什么此标志有用?

有时,您希望确保无论在子类中添加了哪些字段,序列化对象都只会包含原始类型定义中列出的字段。如果您在子类中添加了类似 password: str 字段之类的内容,而您不想意外地将其包含在序列化输出中,这将非常有用。

例如

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user1: User
    user2: User


user = UserLogin(name='pydantic', password='password')

outer_model = OuterModel(user1=user, user2=user)
print(outer_model.model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user1': {'name': 'pydantic', 'password': 'password'},
    'user2': {'name': 'pydantic', 'password': 'password'},
}
"""

print(outer_model.model_dump(serialize_as_any=False))  # (2)!
#> {'user1': {'name': 'pydantic'}, 'user2': {'name': 'pydantic'}}
  1. serialize_as_any 设置为 True 后,结果与 V1 的结果匹配。
  2. serialize_as_any 设置为 False(V2 默认值)后,子类上存在但基类上不存在的字段不会包含在序列化中。

此设置甚至对嵌套和递归模式也有效。例如

from pydantic import BaseModel


class User(BaseModel):
    name: str
    friends: list['User']


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(
    name='samuel',
    password='pydantic-pw',
    friends=[UserLogin(name='sebastian', password='fastapi-pw', friends=[])],
)

print(OuterModel(user=user).model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user': {
        'name': 'samuel',
        'friends': [
            {'name': 'sebastian', 'friends': [], 'password': 'fastapi-pw'}
        ],
        'password': 'pydantic-pw',
    }
}
"""

print(OuterModel(user=user).model_dump(serialize_as_any=False))  # (2)!
"""
{'user': {'name': 'samuel', 'friends': [{'name': 'sebastian', 'friends': []}]}}
"""
  1. 即使是嵌套的 User 模型实例也会转储 User 子类独有的字段。
  2. 即使是嵌套的 User 模型实例也会转储不包含 User 子类独有字段的字段。

注意

serialize_as_any 运行时标志的行为与 SerializeAsAny 注解的行为几乎相同。我们正在努力解决一些细微的差异,但在大多数情况下,您可以期望两者具有相同的行为。有关差异的更多信息,请参阅此 活动问题

覆盖 serialize_as_any 默认值 (False)

您可以通过配置 BaseModel 的子类来覆盖 serialize_as_any 的默认设置,该子类覆盖 model_dump()model_dump_json()serialize_as_any 参数的默认值,然后将其用作您希望具有此默认行为的任何模型的基础类(而不是 pydantic.BaseModel)。

例如,如果您想默认使用鸭子类型序列化,可以执行以下操作

from typing import Any

from pydantic import BaseModel, SecretStr


class MyBaseModel(BaseModel):
    def model_dump(self, **kwargs) -> dict[str, Any]:
        return super().model_dump(serialize_as_any=True, **kwargs)

    def model_dump_json(self, **kwargs) -> str:
        return super().model_dump_json(serialize_as_any=True, **kwargs)


class User(MyBaseModel):
    name: str


class UserInfo(User):
    password: SecretStr


class OuterModel(MyBaseModel):
    user: User


u = OuterModel(user=UserInfo(name='John', password='secret_pw'))
print(u.model_dump_json())  # (1)!
#> {"user":{"name":"John","password":"**********"}}
  1. 默认情况下,model_dump_json 将使用鸭子类型序列化行为,这意味着 password 字段包含在输出中。

pickle.dumps(model)

Pydantic 模型支持高效的 pickle 序列化和反序列化。

import pickle

from pydantic import BaseModel


class FooBarModel(BaseModel):
    a: str
    b: int


m = FooBarModel(a='hello', b=123)
print(m)
#> a='hello' b=123
data = pickle.dumps(m)
print(data[:20])
#> b'\x80\x04\x95\x95\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main_'
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123

高级包含和排除

model_dumpmodel_dump_json 方法支持 includeexclude 参数,这些参数可以是集合或字典。这允许嵌套选择要导出的字段

from pydantic import BaseModel, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr


class Transaction(BaseModel):
    id: str
    user: User
    value: int


t = Transaction(
    id='1234567890',
    user=User(id=42, username='JohnDoe', password='hashedpassword'),
    value=9876543210,
)

# using a set:
print(t.model_dump(exclude={'user', 'value'}))
#> {'id': '1234567890'}

# using a dict:
print(t.model_dump(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}

print(t.model_dump(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}

使用 True 表示我们要排除或包含整个键,就像将其包含在集合中一样(请注意,不支持使用 False)。这可以在任何深度级别完成。

从子模型或字典的列表或元组中包含或排除字段时,必须格外小心。在这种情况下,model_dump 和相关方法期望元素级包含或排除的整数键。要从列表或元组的每个成员中排除字段,可以使用字典键 '__all__',如下所示

import datetime

from pydantic import BaseModel, SecretStr


class Country(BaseModel):
    name: str
    phone_code: int


class Address(BaseModel):
    post_code: int
    country: Country


class CardDetails(BaseModel):
    number: SecretStr
    expires: datetime.date


class Hobby(BaseModel):
    name: str
    info: str


class User(BaseModel):
    first_name: str
    second_name: str
    address: Address
    card_details: CardDetails
    hobbies: list[Hobby]


user = User(
    first_name='John',
    second_name='Doe',
    address=Address(
        post_code=123456, country=Country(name='USA', phone_code=1)
    ),
    card_details=CardDetails(
        number='4212934504460000', expires=datetime.date(2020, 5, 1)
    ),
    hobbies=[
        Hobby(name='Programming', info='Writing code and stuff'),
        Hobby(name='Gaming', info='Hell Yeah!!!'),
    ],
)

exclude_keys = {
    'second_name': True,
    'address': {'post_code': True, 'country': {'phone_code'}},
    'card_details': True,
    # You can exclude fields from specific members of a tuple/list by index:
    'hobbies': {-1: {'info'}},
}

include_keys = {
    'first_name': True,
    'address': {'country': {'name'}},
    'hobbies': {0: True, -1: {'name'}},
}

# would be the same as user.model_dump(exclude=exclude_keys) in this case:
print(user.model_dump(include=include_keys))
"""
{
    'first_name': 'John',
    'address': {'country': {'name': 'USA'}},
    'hobbies': [
        {'name': 'Programming', 'info': 'Writing code and stuff'},
        {'name': 'Gaming'},
    ],
}
"""

# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.model_dump(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
    'first_name': 'John',
    'second_name': 'Doe',
    'address': {
        'post_code': 123456,
        'country': {'name': 'USA', 'phone_code': 1},
    },
    'card_details': {
        'number': SecretStr('**********'),
        'expires': datetime.date(2020, 5, 1),
    },
    'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""

model_dump_json 方法也是如此。

模型和字段级别的包含和排除

除了传递给 model_dumpmodel_dump_json 方法的显式 excludeinclude 参数之外,我们还可以将 exclude: bool 参数直接传递给 Field 构造函数

在字段构造函数上设置 exclude (Field(exclude=True)) 优先于 model_dumpmodel_dump_json 上的 exclude/include

from pydantic import BaseModel, Field, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr = Field(exclude=True)


class Transaction(BaseModel):
    id: str
    value: int = Field(exclude=True)


t = Transaction(
    id='1234567890',
    value=9876543210,
)

print(t.model_dump())
#> {'id': '1234567890'}
print(t.model_dump(include={'id': True, 'value': True}))  # (1)!
#> {'id': '1234567890'}
  1. value 从输出中排除,因为它在 Field 中被排除。

话虽如此,在字段构造函数上设置 exclude (Field(exclude=True)) 不优先于 model_dumpmodel_dump_json 上的 exclude_unsetexclude_noneexclude_default 参数

from typing import Optional

from pydantic import BaseModel, Field


class Person(BaseModel):
    name: str
    age: Optional[int] = Field(None, exclude=False)


person = Person(name='Jeremy')

print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True))  # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True))  # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True))  # (3)!
#> {'name': 'Jeremy'}
  1. age 从输出中排除,因为 exclude_none 设置为 True,并且 ageNone
  2. age 从输出中排除,因为 exclude_unset 设置为 True,并且 age 未在 Person 构造函数中设置。
  3. age 从输出中排除,因为 exclude_defaults 设置为 True,并且 age 采用默认值 None
from pydantic import BaseModel, Field


class Person(BaseModel):
    name: str
    age: int | None = Field(None, exclude=False)


person = Person(name='Jeremy')

print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True))  # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True))  # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True))  # (3)!
#> {'name': 'Jeremy'}
  1. age 从输出中排除,因为 exclude_none 设置为 True,并且 ageNone
  2. age 从输出中排除,因为 exclude_unset 设置为 True,并且 age 未在 Person 构造函数中设置。
  3. age 从输出中排除,因为 exclude_defaults 设置为 True,并且 age 采用默认值 None

序列化上下文

您可以将上下文对象传递给序列化方法,可以从装饰的序列化器函数的 info 参数访问该上下文对象。当您需要在运行时动态更新序列化行为时,这非常有用。例如,如果您希望根据动态可控制的允许值集转储字段,则可以通过上下文传递允许值来完成此操作

from pydantic import BaseModel, SerializationInfo, field_serializer


class Model(BaseModel):
    text: str

    @field_serializer('text')
    def remove_stopwords(self, v: str, info: SerializationInfo):
        context = info.context
        if context:
            stopwords = context.get('stopwords', set())
            v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
        return v


model = Model.model_construct(**{'text': 'This is an example document'})
print(model.model_dump())  # no context
#> {'text': 'This is an example document'}
print(model.model_dump(context={'stopwords': ['this', 'is', 'an']}))
#> {'text': 'example document'}
print(model.model_dump(context={'stopwords': ['document']}))
#> {'text': 'This is an example'}

类似地,您可以使用上下文进行验证

model_copy(...)

API 文档

pydantic.main.BaseModel.model_copy

model_copy() 允许复制模型(带有可选更新),这在处理冻结模型时特别有用。

示例

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(m.model_copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar) == id(m.model_copy().bar))
#> True
# normal copy gives the same object reference for bar
print(id(m.bar) == id(m.model_copy(deep=True).bar))
#> False
# deep copy gives a new object reference for `bar`