序列化
除了直接通过字段名称(例如 model.foobar
)访问模型属性之外,模型还可以通过多种方式进行转换、转储、序列化和导出。
序列化与转储
Pydantic 交替使用术语“序列化”和“转储”。两者都指将模型转换为字典或 JSON 编码字符串的过程。
在 Pydantic 之外,“序列化”一词通常指将内存中的数据转换为字符串或字节。但是,在 Pydantic 的上下文中,将对象从更结构化的形式(例如 Pydantic 模型、数据类等)转换为由 Python 内置类型(如 dict)组成的结构化程度较低的形式之间存在非常密切的关系。
虽然我们可以(并且偶尔会)通过在转换为原始类型时使用“转储”一词,在转换为字符串时使用“序列化”来区分这些情况,但出于实际目的,我们经常使用“序列化”一词来指代这两种情况,即使它并不总是意味着转换为字符串或字节。
model.model_dump(...)
¶
这是将模型转换为字典的主要方法。子模型将被递归转换为字典。
默认情况下,输出可能包含非 JSON 可序列化的 Python 对象。可以将 mode
参数指定为 'json'
,以确保输出仅包含 JSON 可序列化类型。还存在其他参数来包含或排除字段,包括嵌套字段,或进一步自定义序列化行为。
有关更多信息,请参阅可用的 参数。
注意
子模型被转换为字典的一个例外是,RootModel
及其子类将直接转储 root
字段值,而没有包装字典。这也是递归完成的。
注意
您可以使用 计算字段 将 property
和 cached_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(...)
¶
.model_dump_json()
方法直接将模型序列化为 JSON 编码的字符串,该字符串等效于 .model_dump()
生成的结果。
有关更多信息,请参阅可用的 参数。
注意
Pydantic 可以将许多常用的类型序列化为 JSON,否则这些类型与简单的 json.dumps(foobar)
(例如 datetime
、date
或 UUID
)不兼容。
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
装饰器,也可以在所有字段上调用单个序列化器。
此外,PlainSerializer
和 WrapSerializer
使您可以使用函数来修改序列化的输出。
这两个序列化器都接受可选参数,包括
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
作为关键字参数传递给 BaseModel
和 RootModel
的 model_dump()
和 model_dump_json
方法。也可以将其作为关键字参数传递给 TypeAdapter
的 dump_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'}}
- 将
serialize_as_any
设置为True
后,结果与 V1 的结果匹配。 - 将
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': []}]}}
"""
- 即使是嵌套的
User
模型实例也会转储User
子类独有的字段。 - 即使是嵌套的
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":"**********"}}
- 默认情况下,
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_dump
和 model_dump_json
方法支持 include
和 exclude
参数,这些参数可以是集合或字典。这允许嵌套选择要导出的字段
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_dump
和 model_dump_json
方法的显式 exclude
和 include
参数之外,我们还可以将 exclude: bool
参数直接传递给 Field
构造函数
在字段构造函数上设置 exclude
(Field(exclude=True)
) 优先于 model_dump
和 model_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'}
value
从输出中排除,因为它在Field
中被排除。
话虽如此,在字段构造函数上设置 exclude
(Field(exclude=True)
) 不优先于 model_dump
和 model_dump_json
上的 exclude_unset
、exclude_none
和 exclude_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'}
age
从输出中排除,因为exclude_none
设置为True
,并且age
为None
。age
从输出中排除,因为exclude_unset
设置为True
,并且age
未在 Person 构造函数中设置。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'}
age
从输出中排除,因为exclude_none
设置为True
,并且age
为None
。age
从输出中排除,因为exclude_unset
设置为True
,并且age
未在 Person 构造函数中设置。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(...)
¶
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`