模型导出
除了通过名称直接访问模型属性(例如 model.foobar
)之外,还可以通过多种方式转换和导出模型:
model.dict(...)
¶
这是将模型转换为字典的主要方式。 子模型将递归地转换为字典。
参数:
include
: 要包含在返回字典中的字段; 见 下文exclude
: 从返回的字典中排除的字段; 见 下文by_alias
: 字段别名是否应该用作返回字典中的键; 默认false
exclude_unset
: 是否应从返回的字典中排除在创建模型时未明确设置的字段; 默认false
。 在 v1.0 之前,exclude_unset
被称为skip_defaults
;skip_defaults
的使用现已弃用exclude_defaults
: 是否应从返回的字典中排除等于其默认值(无论是否设置)的字段; 默认“假”exclude_none
: 是否应从返回的字典中排除等于None
的字段; 默认false
。
例子:
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})
# returns a dictionary:
print(m.dict())
"""
{
'banana': 3.14,
'foo': 'hello',
'bar': {'whatever': 123},
}
"""
print(m.dict(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.dict(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
(这个脚本是完整的,它应该“按原样”运行)
dict(model)
和迭代(dict(model)
and iteration)¶
pydantic 模型也可以使用 dict(model)
转换为字典,您还可以使用 for field_name, 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
(这个脚本是完整的,它应该“按原样”运行)
model.copy(...)
¶
copy()
允许复制模型,这对于不可变模型特别有用。
参数:
include
: 要包含在返回字典中的字段; 见 下文exclude
: 从返回的字典中排除的字段; 见 下文update
: 创建复制模型时要更改的值字典deep
: 是否对新模型进行深拷贝; 默认false
。
例子:
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.copy(include={'foo', 'bar'}))
#> foo='hello' bar=BarModel(whatever=123)
print(m.copy(exclude={'foo', 'bar'}))
#> banana=3.14
print(m.copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar), id(m.copy().bar))
#> 140382179354000 140382179354000
# normal copy gives the same object reference for `bar`
print(id(m.bar), id(m.copy(deep=True).bar))
#> 140382179354000 140382179355600
# deep copy gives a new object reference for `bar`
(这个脚本是完整的,它应该“按原样”运行)
model.json(...)
¶
The .json()
method will serialise a model to JSON. (For models with a custom root type,
only the value for the __root__
key is serialised)
参数:
include
: 要包含在返回字典中的字段; 见 下文exclude
: 从返回的字典中排除的字段; 见 下文by_alias
: 字段别名是否应该用作返回字典中的键; 默认false
。exclude_unset
: 是否应从返回的字典中排除在创建模型时未设置且具有默认值的字段; 默认false
。 在 v1.0 之前,exclude_unset
被称为skip_defaults
;skip_defaults
的使用现已弃用exclude_defaults
: 是否应从返回的字典中排除等于其默认值(无论是否设置)的字段; 默认false
。exclude_none
: 是否应从返回的字典中排除等于None
的字段; 默认false
。encoder
: 传递给json.dumps()
的default
参数的自定义编码器函数; 默认为自定义编码器,旨在处理所有常见类型。**dumps_kwargs
: 任何其他关键字参数都传递给json.dumps()
,例如indent
。
pydantic 可以将许多常用类型序列化为 JSON(例如datetime
、date
或UUID
),这通常会因简单的json.dumps(foobar)
而失败。
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.json())
#> {"foo": "2032-06-01T12:13:14", "bar": {"whatever": 123}}
(这个脚本是完整的,它应该“按原样”运行)
json_encoders
¶
可以使用 json_encoders
配置属性在模型上自定义序列化; 键应该是类型(或前向引用的类型名称),值应该是序列化该类型的函数(参见下面的示例):
from datetime import datetime, timedelta
from pydantic import BaseModel
from pydantic.json import timedelta_isoformat
class WithCustomEncoders(BaseModel):
dt: datetime
diff: timedelta
class Config:
json_encoders = {
datetime: lambda v: v.timestamp(),
timedelta: timedelta_isoformat,
}
m = WithCustomEncoders(dt=datetime(2032, 6, 1), diff=timedelta(hours=100))
print(m.json())
#> {"dt": 1969632000.0, "diff": "P4DT4H0M0.000000S"}
(这个脚本是完整的,它应该“按原样”运行)
默认情况下,timedelta
被编码为总秒数的简单浮点数。 timedelta_isoformat
作为一个可选的替代方案提供,它实现了 ISO 8601 时间差异编码。
json_encoders
也在模型继承期间合并,子编码器优先于父编码器。
from datetime import datetime, timedelta
from pydantic import BaseModel
from pydantic.json import timedelta_isoformat
class BaseClassWithEncoders(BaseModel):
dt: datetime
diff: timedelta
class Config:
json_encoders = {
datetime: lambda v: v.timestamp()
}
class ChildClassWithEncoders(BaseClassWithEncoders):
class Config:
json_encoders = {
timedelta: timedelta_isoformat
}
m = ChildClassWithEncoders(dt=datetime(2032, 6, 1), diff=timedelta(hours=100))
print(m.json())
#> {"dt": 1969632000.0, "diff": "P4DT4H0M0.000000S"}
(这个脚本是完整的,它应该“按原样”运行)
序列化自引用或其他模型(Serialising self-reference or other models)¶
默认情况下,模型被序列化为字典。
如果你想以不同的方式序列化它们,你可以在调用 json()
方法时添加 models_as_dict=False
并在 json_encoders
中添加模型的类。
在前向引用的情况下,您可以使用带有类名的字符串而不是类本身
from typing import List, Optional
from pydantic import BaseModel
class Address(BaseModel):
city: str
country: str
class User(BaseModel):
name: str
address: Address
friends: Optional[List['User']] = None
class Config:
json_encoders = {
Address: lambda a: f'{a.city} ({a.country})',
'User': lambda u: f'{u.name} in {u.address.city} '
f'({u.address.country[:2].upper()})',
}
User.update_forward_refs()
wolfgang = User(
name='Wolfgang',
address=Address(city='Berlin', country='Deutschland'),
friends=[
User(name='Pierre', address=Address(city='Paris', country='France')),
User(name='John', address=Address(city='London', country='UK')),
],
)
print(wolfgang.json(models_as_dict=False))
#> {"name": "Wolfgang", "address": "Berlin (Deutschland)", "friends": ["Pierre
#> in Paris (FR)", "John in London (UK)"]}
from typing import Optional
from pydantic import BaseModel
class Address(BaseModel):
city: str
country: str
class User(BaseModel):
name: str
address: Address
friends: Optional[list['User']] = None
class Config:
json_encoders = {
Address: lambda a: f'{a.city} ({a.country})',
'User': lambda u: f'{u.name} in {u.address.city} '
f'({u.address.country[:2].upper()})',
}
User.update_forward_refs()
wolfgang = User(
name='Wolfgang',
address=Address(city='Berlin', country='Deutschland'),
friends=[
User(name='Pierre', address=Address(city='Paris', country='France')),
User(name='John', address=Address(city='London', country='UK')),
],
)
print(wolfgang.json(models_as_dict=False))
#> {"name": "Wolfgang", "address": "Berlin (Deutschland)", "friends": ["Pierre
#> in Paris (FR)", "John in London (UK)"]}
from pydantic import BaseModel
class Address(BaseModel):
city: str
country: str
class User(BaseModel):
name: str
address: Address
friends: list['User'] | None = None
class Config:
json_encoders = {
Address: lambda a: f'{a.city} ({a.country})',
'User': lambda u: f'{u.name} in {u.address.city} '
f'({u.address.country[:2].upper()})',
}
User.update_forward_refs()
wolfgang = User(
name='Wolfgang',
address=Address(city='Berlin', country='Deutschland'),
friends=[
User(name='Pierre', address=Address(city='Paris', country='France')),
User(name='John', address=Address(city='London', country='UK')),
],
)
print(wolfgang.json(models_as_dict=False))
#> {"name": "Wolfgang", "address": "Berlin (Deutschland)", "friends": ["Pierre
#> in Paris (FR)", "John in London (UK)"]}
(这个脚本是完整的,它应该“按原样”运行)
序列化子类(Serialising subclasses)¶
Note
版本 v1.5 中的新功能。
在 v1.5 之前,普通类型的子类不会自动序列化为 JSON。
公共类型的子类像它们的超类一样自动编码:
from datetime import date, timedelta
from pydantic import BaseModel
from pydantic.validators import int_validator
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_validators__(cls):
yield int_validator
yield cls.validate
@classmethod
def validate(cls, v: int):
return date.today().replace(month=1, day=1) + timedelta(days=v)
class FooModel(BaseModel):
date: DayThisYear
m = FooModel(date=300)
print(m.json())
#> {"date": "2023-10-28"}
(这个脚本是完整的,它应该“按原样”运行)
自定义 JSON(反)序列化(Custom JSON (de)serialisation)¶
为了提高编码和解码 JSON 的性能,可以通过 Config
的 json_loads
和 json_dumps
属性使用替代 JSON 实现(例如 ujson) `。
from datetime import datetime
import ujson
from pydantic import BaseModel
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
class Config:
json_loads = ujson.loads
user = User.parse_raw('{"id": 123,"signup_ts":1234567890,"name":"John Doe"}')
print(user)
#> id=123 signup_ts=datetime.datetime(2009, 2, 13, 23, 31, 30,
#> tzinfo=datetime.timezone.utc) name='John Doe'
(这个脚本是完整的,它应该“按原样”运行)
ujson
通常不能用于转储 JSON,因为它不支持日期时间等对象的编码,并且不接受 default
回退函数参数。 为此,您可以使用另一个库,例如 orjson。
from datetime import datetime
import orjson
from pydantic import BaseModel
def orjson_dumps(v, *, default):
# orjson.dumps returns bytes, to match standard json.dumps we need to decode
return orjson.dumps(v, default=default).decode()
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
class Config:
json_loads = orjson.loads
json_dumps = orjson_dumps
user = User.parse_raw('{"id":123,"signup_ts":1234567890,"name":"John Doe"}')
print(user.json())
#> {"id":123,"signup_ts":"2009-02-13T23:31:30+00:00","name":"John Doe"}
(这个脚本是完整的,它应该“按原样”运行)
请注意,orjson
本身负责处理 datetime
编码,使其比 json.dumps
更快,但这意味着您不能总是使用 Config.json_encoders
自定义编码。
pickle.dumps(model)
¶
使用与 copy()
相同的方案,pydantic 模型支持高效的 pickling 和 unpickling。
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)
"""
b'\x80\x04\x95\x8e\x00\x00\x00\x00\x00\x00\x00\x8c\x17exporting_models_pickle
\x94\x8c\x0bFooBarModel\x94\x93\x94)\x81\x94}\x94(\x8c\x08__dict__\x94}\x94(\
x8c\x01a\x94\x8c\x05hello\x94\x8c\x01b\x94K{u\x8c\x0e__fields_set__\x94\x8f\x
94(h\th\x07\x90\x8c\x1c__private_attribute_values__\x94}\x94ub.'
"""
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123
(这个脚本是完整的,它应该“按原样”运行)
高级包含和排除(Advanced include and exclude)¶
dict
、json
和 copy
方法支持 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.dict(exclude={'user', 'value'}))
#> {'id': '1234567890'}
# using a dict:
print(t.dict(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}
print(t.dict(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}
(这个脚本是完整的,它应该“按原样”运行)
True
表示我们想要排除或包含整个键,就好像我们将它包含在一个集合中一样。 当然,可以在任何深度级别进行相同的操作。
在从子模型或字典的列表或元组中包含或排除字段时必须特别小心。 在这种情况下,dict
和相关方法需要整数键来按元素包含或排除。 要从列表或元组的每个成员中排除一个字段,可以使用字典键__all__
,如下所示:
import datetime
from typing import List
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.dict(exclude=exclude_keys) in this case:
print(user.dict(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.dict(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'}],
}
"""
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.dict(exclude=exclude_keys) in this case:
print(user.dict(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.dict(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'}],
}
"""
(这个脚本是完整的,它应该“按原样”运行)
json
和 copy
方法也是如此。
模型和字段级别包含和排除(Model and field level include and exclude)¶
除了传递给 dict
、json
和 copy
方法的显式参数 exclude
和 include
之外,我们还可以将 include
/exclude
参数直接传递给 Field
构造函数或 模型Config
类中的等效Field
实例:
from pydantic import BaseModel, Field, SecretStr
class User(BaseModel):
id: int
username: str
password: SecretStr = Field(..., exclude=True)
class Transaction(BaseModel):
id: str
user: User = Field(..., exclude={'username'})
value: int
class Config:
fields = {'value': {'exclude': True}}
t = Transaction(
id='1234567890',
user=User(
id=42,
username='JohnDoe',
password='hashedpassword'
),
value=9876543210,
)
print(t.dict())
#> {'id': '1234567890', 'user': {'id': 42}}
(这个脚本是完整的,它应该“按原样”运行)
在使用多种策略的情况下,exclude
/include
字段按照以下规则进行合并:
- 首先,模型配置级别设置(通过
Field
实例)按字段与字段构造器设置(即Field(..., exclude=True)
)合并,字段构造器优先。 - 结果设置按类与
dict
、json
、copy
调用的显式设置合并,显式设置优先。
请注意,在合并设置时,exclude
通过计算键的union
合并,而include
通过计算键的交集(intersection)
合并。
生成的合并排除设置:
from pydantic import BaseModel, Field, SecretStr
class User(BaseModel):
id: int
username: str # overridden by explicit exclude
password: SecretStr = Field(exclude=True)
class Transaction(BaseModel):
id: str
user: User
value: int
t = Transaction(
id='1234567890',
user=User(
id=42,
username='JohnDoe',
password='hashedpassword'
),
value=9876543210,
)
print(t.dict(exclude={'value': True, 'user': {'username'}}))
#> {'id': '1234567890', 'user': {'id': 42}}
(这个脚本是完整的,它应该“按原样”运行)
与使用合并包含设置相同,如下所示:
from pydantic import BaseModel, Field, SecretStr
class User(BaseModel):
id: int = Field(..., include=True)
username: str = Field(..., include=True) # overridden by explicit include
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,
)
print(t.dict(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}
(这个脚本是完整的,它应该“按原样”运行)