Pydantic 序列化¶
Tortoise ORM 具有一个 Pydantic 插件,该插件将从 Tortoise 模型生成 Pydantic 模型,然后提供帮助程序函数来序列化该模型及其相关对象。
我们目前仅支持为序列化生成 Pydantic 对象,而不支持在此阶段进行反序列化。
请参阅 Pydantic 示例
教程¶
1:基本用法¶
这里我们介绍
从 Tortoise 模型创建 Pydantic 模型
使用文档字符串和文档注释
评估生成的架构
使用
.model_dump()
和.model_dump_json()
两种方法进行简单序列化
示例源代码:1:基本用法
让我们从一个基本的 Tortoise 模型开始
from tortoise import fields
from tortoise.models import Model
class Tournament(Model):
"""
This references a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
#: The date-time the Tournament record was created at
created_at = fields.DatetimeField(auto_now_add=True)
from tortoise.contrib.pydantic import pydantic_model_creator
Tournament_Pydantic = pydantic_model_creator(Tournament)
现在,我们有了一个 Pydantic 模型,可用于表示架构和序列化。
现在,Tournament_Pydantic
的 JSON 架构为
>>> print(Tournament_Pydantic.schema())
{
'title': 'Tournament',
'description': 'This references a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'description': 'The date-time the Tournament record was created at',
'type': 'string',
'format': 'date-time'
}
}
}
请注意,类文档字符串和文档注释 #:
如何作为架构中的描述包含在内。
要序列化一个对象,只需(在异步上下文中)
tournament = await Tournament.create(name="New Tournament")
tourpy = await Tournament_Pydantic.from_tortoise_orm(tournament)
可以使用 常规 Pydantic 对象方法(例如 .model_dump()
或 .model_dump_json()
)获取内容
>>> print(tourpy.model_dump())
{
'id': 1,
'name': 'New Tournament',
'created_at': datetime.datetime(2020, 3, 1, 20, 28, 9, 346808)
}
>>> print(tourpy.model_dump_json())
{
"id": 1,
"name": "New Tournament",
"created_at": "2020-03-01T20:28:09.346808"
}
2:查询集和列表¶
这里我们介绍
创建列表模型以序列化查询集
默认排序得到保留
示例源代码:2:查询集和列表
from tortoise import fields
from tortoise.models import Model
class Tournament(Model):
"""
This references a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
#: The date-time the Tournament record was created at
created_at = fields.DatetimeField(auto_now_add=True)
class Meta:
# Define the default ordering
# the pydantic serialiser will use this to order the results
ordering = ["name"]
from tortoise.contrib.pydantic import pydantic_queryset_creator
Tournament_Pydantic_List = pydantic_queryset_creator(Tournament)
现在,我们有了一个 Pydantic 模型,可用于表示架构和序列化。
现在,Tournament_Pydantic_List
的 JSON 架构为
>>> print(Tournament_Pydantic_List.schema())
{
'title': 'Tournaments',
'description': 'This references a Tournament',
'type': 'array',
'items': {
'$ref': '#/definitions/Tournament'
},
'definitions': {
'Tournament': {
'title': 'Tournament',
'description': 'This references a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'description': 'The date-time the Tournament record was created at',
'type': 'string',
'format': 'date-time'
}
}
}
}
}
请注意,Tournament
现在不是根。一个简单的列表是根。
要序列化一个对象,只需(在异步上下文中)
# Create objects
await Tournament.create(name="New Tournament")
await Tournament.create(name="Another")
await Tournament.create(name="Last Tournament")
tourpy = await Tournament_Pydantic_List.from_queryset(Tournament.all())
可以使用 常规 Pydantic 对象方法(例如 .model_dump()
或 .model_dump_json()
)获取内容
>>> print(tourpy.model_dump())
{
'root': [
{
'id': 2,
'name': 'Another',
'created_at': datetime.datetime(2020, 3, 2, 6, 53, 39, 776504)
},
{
'id': 3,
'name': 'Last Tournament',
'created_at': datetime.datetime(2020, 3, 2, 6, 53, 39, 776848)
},
{
'id': 1,
'name': 'New Tournament',
'created_at': datetime.datetime(2020, 3, 2, 6, 53, 39, 776211)
}
]
}
>>> print(tourpy.model_dump_json())
[
{
"id": 2,
"name": "Another",
"created_at": "2020-03-02T06:53:39.776504"
},
{
"id": 3,
"name": "Last Tournament",
"created_at": "2020-03-02T06:53:39.776848"
},
{
"id": 1,
"name": "New Tournament",
"created_at": "2020-03-02T06:53:39.776211"
}
]
请注意,.model_dump()
有一个包含列表的 root
元素,但 .model_dump_json()
将列表作为根。还要注意,结果如何按 name
按字母顺序排序。
3:关系和早期初始化¶
这里我们介绍
关系
早期模型初始化
注意
本教程中关于早期初始化的部分仅在需要在初始化 Tortoise ORM 之前生成 Pydantic 模型时才需要。
查看 基本 Pydantic(在函数 run
中)以查看 *_creator is only
仅在正确初始化 Tortoise ORM 之后才被调用,在这种情况下,不需要早期初始化。
示例源代码:3:关系和早期初始化
我们使用关系定义模型
from tortoise import fields
from tortoise.models import Model
class Tournament(Model):
"""
This references a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
#: The date-time the Tournament record was created at
created_at = fields.DatetimeField(auto_now_add=True)
class Event(Model):
"""
This references an Event in a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
created_at = fields.DatetimeField(auto_now_add=True)
tournament = fields.ForeignKeyField(
"models.Tournament", related_name="events", description="The Tournament this happens in"
)
接下来,我们使用 pydantic_model_creator
创建 Pydantic 模型
from tortoise.contrib.pydantic import pydantic_model_creator
Tournament_Pydantic = pydantic_model_creator(Tournament)
现在,Tournament_Pydantic
的 JSON 架构为
>>> print(Tournament_Pydantic.schema())
{
'title': 'Tournament',
'description': 'This references a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'description': 'The date-time the Tournament record was created at',
'type': 'string',
'format': 'date-time'
}
}
}
哦,不!关系在哪里?
由于模型尚未完全初始化,因此它在此阶段不知道关系。
我们需要使用 tortoise.Tortoise.init_models()
早期初始化我们的模型关系
from tortoise import Tortoise
Tortoise.init_models(["__main__"], "models")
# Now lets try again
Tournament_Pydantic = pydantic_model_creator(Tournament)
现在,Tournament_Pydantic
的 JSON 架构为
>>> print(Tournament_Pydantic.schema())
{
'title': 'Tournament',
'description': 'This references a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'description': 'The date-time the Tournament record was created at',
'type': 'string',
'format': 'date-time'
},
'events': {
'title': 'Events',
'description': 'The Tournament this happens in',
'type': 'array',
'items': {
'$ref': '#/definitions/Event'
}
}
},
'definitions': {
'Event': {
'title': 'Event',
'description': 'This references an Event in a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'type': 'string',
'format': 'date-time'
}
}
}
}
}
啊哈!这样好多了。
注意,我们还可以为 Event
以相同的方式创建一个模型,它应该可以正常工作
Event_Pydantic = pydantic_model_creator(Event)
>>> print(Event_Pydantic.schema())
{
'title': 'Event',
'description': 'This references an Event in a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'type': 'string',
'format': 'date-time'
},
'tournament': {
'title': 'Tournament',
'description': 'The Tournament this happens in',
'allOf': [
{
'$ref': '#/definitions/Tournament'
}
]
}
},
'definitions': {
'Tournament': {
'title': 'Tournament',
'description': 'This references a Tournament',
'type': 'object',
'properties': {
'id': {
'title': 'Id',
'type': 'integer'
},
'name': {
'title': 'Name',
'type': 'string'
},
'created_at': {
'title': 'Created At',
'description': 'The date-time the Tournament record was created at',
'type': 'string',
'format': 'date-time'
}
}
}
}
}
并且它还定义了关系!
请注意,两个模式都不遵循关系。这是默认设置,在后面的教程中,我们将展示这些选项。
让我们创建并序列化对象,看看它们的样子(在异步上下文中)
# Create objects
tournament = await Tournament.create(name="New Tournament")
event = await Event.create(name="The Event", tournament=tournament)
# Serialise Tournament
tourpy = await Tournament_Pydantic.from_tortoise_orm(tournament)
>>> print(tourpy.model_dump_json())
{
"id": 1,
"name": "New Tournament",
"created_at": "2020-03-02T07:23:27.731656",
"events": [
{
"id": 1,
"name": "The Event",
"created_at": "2020-03-02T07:23:27.732492"
}
]
}
并序列化事件(在异步上下文中)
eventpy = await Event_Pydantic.from_tortoise_orm(event)
>>> print(eventpy.model_dump_json())
{
"id": 1,
"name": "The Event",
"created_at": "2020-03-02T07:23:27.732492",
"tournament": {
"id": 1,
"name": "New Tournament",
"created_at": "2020-03-02T07:23:27.731656"
}
}
4: PydanticMeta 和可调用对象¶
这里我们介绍
通过
PydanticMeta
类配置模型创建器。使用可调用函数注释额外数据。
让我们添加一些计算数据的方法,并告诉创建者使用它们
class Tournament(Model):
"""
This references a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
created_at = fields.DatetimeField(auto_now_add=True)
# It is useful to define the reverse relations manually so that type checking
# and auto completion work
events: fields.ReverseRelation["Event"]
def name_length(self) -> int:
"""
Computed length of name
"""
return len(self.name)
def events_num(self) -> int:
"""
Computed team size
"""
try:
return len(self.events)
except NoValuesFetched:
return -1
class PydanticMeta:
# Let's exclude the created timestamp
exclude = ("created_at",)
# Let's include two callables as computed columns
computed = ("name_length", "events_num")
class Event(Model):
"""
This references an Event in a Tournament
"""
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
created_at = fields.DatetimeField(auto_now_add=True)
tournament = fields.ForeignKeyField(
"models.Tournament", related_name="events", description="The Tournament this happens in"
)
class Meta:
ordering = ["name"]
class PydanticMeta:
exclude = ("created_at",)
这里有很多东西需要解包。
首先,我们定义了一个 PydanticMeta
块,其中包含用于 pydantic 模型创建器的配置选项。请参阅 tortoise.contrib.pydantic.creator.PydanticMeta
以了解可用选项。
其次,我们在两个模型中都排除了 created_at
,因为我们认为它没有好处。
第三,我们添加了两个可调用对象:name_length
和 events_num
。我们希望它们作为结果集的一部分。请注意,可调用对象/计算字段需要手动指定返回类型,因为如果没有此类型,我们将无法确定创建有效 Pydantic 模式所需的记录类型。对于标准 Tortoise ORM 字段,不需要这样做,因为这些字段已经定义了一个有效的类型。
请注意,Pydantic 序列化器无法调用异步方法,但由于 tortoise 帮助程序预先获取关系数据,因此在序列化之前可以使用它。因此,我们不需要等待关系。然而,我们应该防止没有进行预取的情况,因此捕获并处理 tortoise.exceptions.NoValuesFetched
异常。
接下来,我们使用 pydantic_model_creator
创建 Pydantic 模型
from tortoise import Tortoise
Tortoise.init_models(["__main__"], "models")
Tournament_Pydantic = pydantic_model_creator(Tournament)
现在,Tournament_Pydantic
的 JSON 架构为
{
"title": "Tournament",
"description": "This references a Tournament",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"events": {
"title": "Events",
"description": "The Tournament this happens in",
"type": "array",
"items": {
"$ref": "#/definitions/Event"
}
},
"name_length": {
"title": "Name Length",
"description": "Computes length of name",
"type": "integer"
},
"events_num": {
"title": "Events Num",
"description": "Computes team size.",
"type": "integer"
}
},
"definitions": {
"Event": {
"title": "Event",
"description": "This references an Event in a Tournament",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
}
}
}
}
}
请注意,已删除 created_at
,并添加了 name_length
和 events_num
。
让我们创建并序列化对象,看看它们的样子(在异步上下文中)
# Create objects
tournament = await Tournament.create(name="New Tournament")
await Event.create(name="Event 1", tournament=tournament)
await Event.create(name="Event 2", tournament=tournament)
# Serialise Tournament
tourpy = await Tournament_Pydantic.from_tortoise_orm(tournament)
>>> print(tourpy.model_dump_json())
{
"id": 1,
"name": "New Tournament",
"events": [
{
"id": 1,
"name": "Event 1"
},
{
"id": 2,
"name": "Event 2"
}
],
"name_length": 14,
"events_num": 2
}
创建器¶
-
tortoise.contrib.pydantic.creator.pydantic_model_creator(cls, *, name=
None
, exclude=()
, include=()
, computed=()
, optional=()
, allow_cycles=None
, sort_alphabetically=None
, _stack=()
, exclude_readonly=False
, meta_override=None
, model_config=None
, validators=None
, module='tortoise.contrib.pydantic.creator'
)[source]¶ 根据 Tortoise 模型构建 Pydantic 模型 的函数。
- 参数:¶
- _stack=
()
¶ 跟踪递归的内部参数
- cls¶
Tortoise 模型
- name=
None
¶ 明确指定自定义名称,而不是生成名称。
- exclude=
()
¶ 从提供的模型中排除的额外字段。
- include=
()
¶ 从提供的模型中包含的额外字段。
- computed=
()
¶ 从提供的模型中包含的额外计算字段。
- optional=
()
¶ 为提供的模型提供的额外可选字段。
- allow_cycles=
None
¶ 我们是否允许在生成的模型中出现任何循环?这仅对递归/自引用模型有用。
False
(默认值)的值将阻止任何和所有回溯。- sort_alphabetically=
None
¶ 按字母顺序对参数进行排序,而不是按字段定义顺序进行排序。
默认顺序为
字段定义顺序 +
反向关系的顺序(按发现的顺序) +
计算函数的顺序(按提供的顺序)。
- exclude_readonly=
False
¶ 构建一个排除任何只读字段的子集模型
- meta_override=
None
¶ 用于覆盖模型值的 PydanticMeta 类。
- model_config=
None
¶ 用作 pydantic 配置的自定义配置。
- validators=
None
¶ 验证字段的方法的字典。
- module=
'tortoise.contrib.pydantic.creator'
¶ 模型所属模块的名称。
- 注意:创建的 pydantic 模型使用 config_class 参数和 PydanticMeta 的
config_class 作为其 Config 类的基础(仅在提供时),但它忽略
fields
配置。pydantic_model_creator 将自动通过 include/exclude/computed 参数生成字段。
- _stack=
- 返回类型:¶
Type
[PydanticModel
]
-
tortoise.contrib.pydantic.creator.pydantic_queryset_creator(cls, *, name=
None
, exclude=()
, include=()
, computed=()
, allow_cycles=None
, sort_alphabetically=None
)[source]¶ 用于构建 Pydantic 模型 Tortoise 模型列表的函数。
- 参数:¶
- cls¶
要放入列表中的 Tortoise 模型。
- name=
None
¶ 明确指定自定义名称,而不是生成名称。
当前生成的列表名称很幼稚,只是在单数名称的末尾添加一个“s”。
- exclude=
()
¶ 从提供的模型中排除的额外字段。
- include=
()
¶ 从提供的模型中包含的额外字段。
- computed=
()
¶ 从提供的模型中包含的额外计算字段。
- allow_cycles=
None
¶ 我们是否允许在生成的模型中出现任何循环?这仅对递归/自引用模型有用。
False
(默认值)的值将阻止任何和所有回溯。- sort_alphabetically=
None
¶ 按字母顺序对参数进行排序,而不是按字段定义顺序进行排序。
默认顺序为
字段定义顺序 +
反向关系的顺序(按发现的顺序) +
计算函数的顺序(按提供的顺序)。
- 返回类型:¶
Type
[PydanticListModel
]
PydanticMeta¶
- class tortoise.contrib.pydantic.creator.PydanticMeta[source]¶
PydanticMeta
类用于配置元数据以生成 pydantic 模型。用法
class Foo(Model): ... class PydanticMeta: exclude = ("foo", "baa") computed = ("count_peanuts", )
-
allow_cycles : bool =
False
¶ 允许递归循环 - 这可能会产生大量数据 - 请小心!请将此与
exclude
/include
和合理的max_recursion
结合使用
-
backward_relations : bool =
True
¶ 在没有注释的情况下使用反向关系 - 不推荐,这可能会产生大量不受控制的数据
-
computed : tuple[str, ...] =
()
¶ 可以在这里列出计算字段以在 pydantic 模型中使用
-
exclude : tuple[str, ...] =
('Meta',)
¶ 在此属性中列出的字段将从 pydantic 模型中排除
-
exclude_raw_fields : bool =
True
¶ 如果我们应该排除关系的原始字段(那些具有 _id 后缀的字段)
-
include : tuple[str, ...] =
()
¶ 如果不为空,则只有此属性包含的字段才会出现在 pydantic 模型中
-
max_recursion : int =
3
¶ 允许的最大递归级别
-
model_config : ConfigDict | None =
None
¶ 允许用户为生成的模型指定自定义配置
-
sort_alphabetically : bool =
False
¶ 按字母顺序对字段进行排序。如果未设置(或
False
),则按声明顺序保留字段
-
allow_cycles : bool =
模型类¶
-
class tortoise.contrib.pydantic.base.PydanticListModel(root=
PydanticUndefined
, **data)[source]¶ Tortoise 模型列表的 Pydantic BaseModel
它在通常的 Pydantic 模型属性之上提供了一个额外的方法
- async classmethod from_queryset(queryset)[source]¶
返回一个可序列化的 pydantic 模型实例,其中包含来自所提供查询集的模型列表。
这会自动预取所有关系。
-
model_computed_fields : ClassVar[dict[str, ComputedFieldInfo]] =
{}
¶ 计算字段名称及其对应的 ComputedFieldInfo 对象的字典。
-
model_config : ClassVar[ConfigDict] =
{}
¶ 模型配置,应符合 [ConfigDict][pydantic.config.ConfigDict] 的字典。
-
model_fields : ClassVar[dict[str, FieldInfo]] =
{'root': FieldInfo(annotation=~RootModelRootType, required=True)}
¶ 模型上定义的字段的元数据,字段名到 [FieldInfo][pydantic.fields.FieldInfo] 的映射。
这替换了 Pydantic V1 中的 Model.__fields__。
- class tortoise.contrib.pydantic.base.PydanticModel(**data)[source]¶
Tortoise 对象的 Pydantic BaseModel。
这提供了除通常的 Pydantic 模型属性 之外的额外方法
- async classmethod from_queryset(queryset)[source]¶
返回一个可序列化的 pydantic 模型实例,其中包含来自所提供查询集的模型列表。
这会自动预取所有关系。
- async classmethod from_queryset_single(queryset)[source]¶
返回提供的查询集中单个模型的可序列化 pydantic 模型实例。
这会自动预取所有关系。
- 参数:¶
- queryset : QuerySetSingle¶
基于此 PydanticModel 的模型上的查询集。
- 返回类型:¶
typing_extensions.Self
- async classmethod from_tortoise_orm(obj)[source]¶
返回由提供的模型实例构建的可序列化 pydantic 模型实例。
注意
这将自动预取所有关系。这可能是你想要的。
如果你不想要这个,或者需要一个
sync
方法,请考虑使用.from_orm()
。在这种情况下,你必须自己管理预取,或者使用
tortoise.contrib.pydantic.creator.PydanticMeta
将关系字段排除在模型之外,否则你将收到OperationalError
异常。这是因为
asyncio
框架如何强制 I/O 在显式await
语句中发生。因此,我们只能在等待的方法中进行延迟获取。
-
model_computed_fields : ClassVar[dict[str, ComputedFieldInfo]] =
{}
¶ 计算字段名称及其对应的 ComputedFieldInfo 对象的字典。
-
model_config : ClassVar[ConfigDict] =
{'from_attributes': True}
¶ 模型配置,应符合 [ConfigDict][pydantic.config.ConfigDict] 的字典。
-
model_fields : ClassVar[dict[str, FieldInfo]] =
{}
¶ 模型上定义的字段的元数据,字段名到 [FieldInfo][pydantic.fields.FieldInfo] 的映射。
这替换了 Pydantic V1 中的 Model.__fields__。