- 数据库
- 后端
【免费下载链接】tortoise-orm
Familiar asyncio ORM for python, built with relations in mind
本篇技术指南围绕 Tortoise ORM 官方提供的 Pydantic 序列化插件展开,讲解如何从 Tortoise Model 自动生成 Pydantic Model(JSON Schema),并完成单对象、Queryset、关系字段与计算字段的序列化。读完本文,你将掌握pydantic_model_creator/pydantic_queryset_creator两个核心 Creator 的用法、PydanticMeta的全部配置项,以及PydanticModel系列基类内置的自动预取与序列化方法,可直接用于 FastAPI / Starlette 等 Web 框架的响应模型构建。
插件概览:只做序列化,不做反序列化
Tortoise ORM 内置一个 Pydantic 插件(位于 tortoise/contrib/pydantic/),它能够根据 Tortoise Model 自动生成 Pydantic Model,并提供辅助函数对 Model 实例及其关联对象进行序列化。这一设计让异步 ORM 的模型定义与 API 层的响应 Schema 可以保持同步,无需手工维护两套字段定义。
需要特别说明的是,当前版本只支持生成用于序列化(serialisation)的 Pydantic 对象,暂不支持反序列化(deserialisation)。也就是说,插件不会把入参 JSON 反写回 Tortoise Model 实例,这一点在使用时需要注意。
插件的公开入口非常简单,从 tortoise/contrib/pydantic/init.py 可以看到全部导出符号:
from tortoise.contrib.pydantic.base import PydanticListModel, PydanticModel from tortoise.contrib.pydantic.creator import ( pydantic_model_creator, pydantic_queryset_creator, )即两个 Creator 函数(pydantic_model_creator、pydantic_queryset_creator)和两个基类(PydanticModel、PydanticListModel)。完整的可运行示例集合见 docs/examples/pydantic.rst,对应的实际脚本位于 examples/pydantic/。
教程一:基础用法——从 Tortoise Model 生成 Pydantic Model
第一个教程(可运行源码见 examples/pydantic/tutorial_1.py)覆盖四个要点:
- 从 Tortoise Model 创建 Pydantic Model;
- 类 Docstring 与字段 Doc-comment(
#:)会被自动带入描述; - 查看生成的 JSON Schema;
- 用
.model_dump()与.model_dump_json()两种方式序列化。
先定义一个基础的 Tortoise Model:
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)要生成 Pydantic Model,调用pydantic_model_creator(定义见 creator.py):
from tortoise.contrib.pydantic import pydantic_model_creator Tournament_Pydantic = pydantic_model_creator(Tournament)此时Tournament_Pydantic就是一个标准的 Pydantic Model,可以用于表示 Schema 与序列化。其 JSON-Schema 如下:
>>> 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' } } }注意两点:类 Docstring 成为整个 Schema 的description,字段前的#:Doc-comment 成为对应属性(如created_at)的description。这一行为在源码中由_cleandoc与字段的docstring or description逻辑实现(见 creator.py 与 creator.py)。
兼容性提示:上述
.schema()输出展示的是 Pydantic v1 时代的 Schema 结构。当前仓库使用 Pydantic v2,官方推荐改用.model_json_schema()(见示例脚本中的schema_json与测试中的model_json_schema调用)。Schema 的语义(title / description / properties / format)保持一致。
序列化一个对象非常简单(在异步上下文中):
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" }两种方法的差别在于:.model_dump()返回 Python 原生对象(datetime保持为datetime.datetime实例),而.model_dump_json()返回真正的 JSON 字符串(datetime被序列化为 ISO-8601 字符串)。
from_tortoise_orm是PydanticModel基类提供的异步类方法(见 base.py),它会在序列化前自动预取(prefetch)全部关系字段,这正是它能一步到位序列化关联对象的原因。
教程二:Queryset 与列表序列化
第二个教程(源码见 examples/pydantic/tutorial_2.py)介绍:
- 创建列表型 Model 来序列化一个 Queryset;
- 默认排序(
Meta.ordering)会被序列化器遵守。
给Tournament加上默认排序:
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"]生成列表 Model 需要调用pydantic_queryset_creator(定义见 creator.py):
from tortoise.contrib.pydantic import pydantic_queryset_creator Tournament_Pydantic_List = pydantic_queryset_creator(Tournament)生成的 JSON-Schema 变为一个数组根节点:
>>> print(Tournament_Pydantic_List.schema()) { 'title': 'Tournaments', 'description': 'This references a Tournament', 'type': 'array', 'items': { '$ref': '#/definitions/Tournament' }, 'definitions': { 'Tournament': { ... } # 与单个 Model 的定义一致 } }注意这里的Tournament不再是根节点,根节点是一个简单的数组。从源码实现看,pydantic_queryset_creator内部先调用pydantic_model_creator生成子模型,再用create_model包装出以root: list[submodel]为字段的PydanticListModel(见 creator.py),列表名默认就是子模型名加_list后缀。
序列化 Queryset:
# 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())输出:
>>> 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()直接以列表为根——这是RootModel的固有行为;- 结果按
name字段字母序排列,说明Meta.ordering = ["name"]被完整继承,查询返回的数据已按该默认排序。
教程三:关系字段与 Early Model Init
第三个教程(源码见 examples/pydantic/tutorial_3.py)引入两个关键概念:
- 关系(Relationships)在 Schema 中的呈现;
- 早期模型初始化(Early Model Init)。
教程中特别提醒:只有在需要在 Tortoise ORM 初始化之前就生成 Pydantic Model 时,才需要 early-init 这一节。常规场景(如 examples/pydantic/basic.py 的run()函数)都是在完成Tortoise.init之后再调用*_creator,此时无需 early-init。
定义带关系字段的模型:
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(Tournament),生成的 Schema 里找不到events关系:
>>> 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': '...', 'type': 'string', 'format': 'date-time' } } }原因在于:模型尚未完全初始化时,ORM 元数据里还没有关系信息。这时需要调用Tortoise.init_models提前初始化模型结构:
from tortoise import Tortoise Tortoise.init_models(["__main__"], "models") # Now lets try again Tournament_Pydantic = pydantic_model_creator(Tournament)再次生成的 Schema 中events数组就出现了:
>>> 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': '...', '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': ..., 'name': ..., 'created_at': ... } } } }同样的方式可以为Event生成模型,关系也会正确带上(注意tournament是以allOf/$ref方式引用的单值外键,而非数组):
Event_Pydantic = pydantic_model_creator(Event) # Event_Pydantic 的 Schema 中同样包含 tournament -> #/definitions/Tournament 引用值得强调的是,两个 Schema 默认都不会沿着关系回溯回去(Tournament 不回引 Tournament,Event 的回引也不会导致无限递归),这是默认行为,后续教程会介绍相关配置项。从源码看,这一行为由递归保护逻辑控制:子模型创建时若发现模型已出现在递归栈中,且allow_cycles=False,则返回None并自动把该字段加入exclude(见 creator.py 与 creator.py)。
创建并序列化对象(异步上下文):
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" } ] } # Serialise Event 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" } }from_tortoise_orm之所以能直接输出嵌套关系,是因为基类方法会递归收集所有 Pydantic 子模型对应的 ORM 关系字段(例如events、events__participants这种__链式字段),并调用obj.fetch_related(*fetch_fields)一次性预取(见 base.py)。
教程四:PydanticMeta 配置与计算字段(Callables)
第四个教程(源码见 examples/pydantic/tutorial_4.py)引入两个核心能力:
- 通过
PydanticMeta类配置模型生成行为; - 使用可调用函数(Callables)向 Schema 注入额外计算数据。
修改后的模型定义:
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是模型生成器的配置块,完整可配置项见下文速查表(源码定义在 descriptions.py 的PydanticMetaData);exclude = ("created_at",)从两个模型的 Schema 中去掉了created_at字段;computed = ("name_length", "events_num")把两个方法作为计算列纳入结果集。注意:计算字段必须手动标注返回类型,否则无法推断出生成合法 Pydantic Schema 所需的记录类型;普通 ORM 字段因为自带类型定义,不需要这个步骤。若没有返回类型注解,_process_computed_field会因取不到return注解而直接跳过该字段(见 creator.py)。
关于events_num中的异常处理,官方文档明确指出:Pydantic 序列化器无法调用异步方法,但由于 Tortoise 辅助函数会在序列化前预取关系数据,同步方法中可以直接访问已加载的关系。即便如此,仍需防御“未做预取”的场景,因此代码捕获并处理了tortoise.exceptions.NoValuesFetched异常(返回-1)。如果计算字段在序列化时访问了未预取的关系,源码中的包装器会重新抛出带有提示信息的NoValuesFetched,建议包含该关系让其自动预取,或先手动fetch_related()(见 creator.py)。
生成与输出:
from tortoise import Tortoise Tortoise.init_models(["__main__"], "models") Tournament_Pydantic = pydantic_model_creator(Tournament) # 生成的 JSON-Schema: { "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被加入,且方法的 Docstring 自动成为了计算字段的description(例如 “Computes length of name”)。
实际序列化效果(异步上下文):
tournament = await Tournament.create(name="New Tournament") await Event.create(name="Event 1", tournament=tournament) await Event.create(name="Event 2", tournament=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 }name_length = 14("New Tournament" 的长度)、events_num = 2(两个关联 Event),计算字段工作正常。
Creator API 详解:两个生成函数的全部参数
在教程之外,docs/contrib/pydantic.rst通过 autodoc 列出了两个 Creator 的完整签名。本节结合 creator.py 源码给出精确说明。
pydantic_model_creator
pydantic_model_creator( cls, # Tortoise Model 类 *, # 以下均为关键字参数 name=None, # 显式指定生成的 Pydantic Model 名称 exclude=None, # 额外排除的字段 include=None, # 额外包含的字段 computed=None, # 额外包含的计算字段(方法名) optional=None, # 额外标记为可选的字段 allow_cycles=None, # 是否允许生成的模型中出现循环引用(递归/自引用模型才需要) sort_alphabetically=None, # 是否按字母序排列字段 exclude_readonly=False, # 构建排除所有只读字段的子集模型 meta_override=None, # 用另一个 PydanticMeta 类覆盖模型的配置 model_config=None, # 自定义 Pydantic 配置(ConfigDict) validators=None, # 字段校验器方法字典 module=__name__, # 生成模型所属的模块名 )各参数要点:
name:自定义模型名;不传时默认使用模型的全限定名,若传入了非默认参数,类名会附带一个由字段信息、关系索引、可选字段等计算的短哈希(见 creator.py),以避免同名冲突;allow_cycles:默认False,会阻止一切回溯;开启后对递归/自引用模型有效,但需配合合理的max_recursion;sort_alphabetically:默认字段顺序为“字段定义顺序 + 发现到的反向关系顺序 + 传入的计算函数顺序”;置True后按字母序排列;exclude_readonly:构建排除只读字段(如auto_now_add时间戳)的子集模型,适合用于需要“可写输入”的请求场景;model_config:自定义 Pydantic ConfigDict,会与PydanticMeta.model_config合并,pydantic_model_creator会自动按 include/exclude/computed 生成字段,忽略fields配置。
pydantic_queryset_creator
pydantic_queryset_creator( cls, # Tortoise Model 类 *, # 以下均为关键字参数 name=None, # 自定义列表模型名称 exclude=(), # 额外排除的字段 include=(), # 额外包含的字段 computed=(), # 额外包含的计算字段 allow_cycles=None, # 同 pydantic_model_creator sort_alphabetically=None, # 同 pydantic_model_creator )内部实现是先对cls调用pydantic_model_creator得到子模型,再用create_model包装出根字段为list[submodel]的PydanticListModel(见 creator.py)。列表模型名默认在子模型名后加_list后缀(当前命名逻辑较朴素,仅追加字母s相关规则,需要稳定名称时请显式传name)。
PydanticMeta 配置项速查表
PydanticMeta是写在 Tortoise Model 内部的配置类,所有字段的默认值定义在 descriptions.py,汇总如下:
| 配置项 | 默认值 | 作用 |
|---|---|---|
include | () | 非空时,只有列出的字段会进入生成的 Pydantic Model(白名单模式) |
exclude | ("Meta",) | 列出的字段将从 Pydantic Model 中排除(黑名单模式) |
computed | () | 列出要作为计算字段纳入的方法名 |
backward_relations | True | 是否自动包含未加注解的反向关系;不推荐随意开启,可能产生不受控的海量数据 |
max_recursion | 3 | 允许的最大递归层级 |
allow_cycles | False | 是否允许递归循环(可能导致 HUGE 数据,务必配合exclude/include与合理的max_recursion使用) |
exclude_raw_fields | True | 是否排除关系字段的原始外键列(带_id后缀的字段,如tournament_id) |
sort_alphabetically | False | 是否按字母序排列字段;为False时保持字段声明顺序 |
model_config | None | 允许为生成的模型指定自定义 Pydantic 配置(ConfigDict) |
几点值得注意的源码细节:
exclude_raw_fields=True时,FieldMap.field_map_update会把关系对应的原始外键列从字段映射中弹出(见 creator.py),因此默认 Schema 中你只会看到tournament关系对象,而看不到tournament_id;backward_relations=False时,只有显式注解的反向关系(如events: fields.ReverseRelation["Event"])才会被保留(见 creator.py),这一点在测试 tests/contrib/test_pydantic.py 中有专门覆盖;meta_override参数允许在不修改 Model 定义的前提下,临时替换整套PydanticMeta配置,测试中即用此法关闭反向关系(见 tests/contrib/test_pydantic.py)。
序列化基类:PydanticModel 与 PydanticListModel
生成的模型都继承自 base.py 中的两个基类,它们提供了开箱即用的异步序列化方法。
PydanticModel
PydanticModel是BaseModel的子类,默认开启from_attributes=True配置(见 base.py)。核心方法:
await cls.from_tortoise_orm(obj):从单个 Model 实例构造可序列化的 Pydantic 实例。会自动预取全部关系,是大多数场景的首选;如果不想预取、或需要同步方法,可改用.from_orm()自行管理预取,否则在序列化时访问未加载的关系会抛出OperationalError(参见该方法的 docstring,见 base.py);await cls.from_queryset(queryset):从 Queryset 构造列表(list[Self]),同样自动预取全部关系(见 base.py);await cls.from_queryset_single(queryset):从查询单个对象的 Queryset 构造实例(见 base.py)。
PydanticListModel
PydanticListModel是RootModel的子类,根字段为root: list[submodel](见 base.py)。核心方法:
await cls.from_queryset(queryset):从 Queryset 构造列表实例,内部委托给子模型的_get_fetch_fields收集预取字段后统一prefetch_related。这解释了教程二中.model_dump()带root键而.model_dump_json()直接输出数组的行为。
另外,PydanticModel的 wrap 模式校验器会把原始 ORM 对象暂存到实例的__orm_obj__属性上(见 base.py),计算字段的包装函数正是通过读取该属性调用原始模型方法(见 creator.py)。
可运行示例与测试验证
仓库提供了完整可运行的示例脚本(全部位于 examples/pydantic/):
| 示例文件 | 演示内容 |
|---|---|
| basic.py | 综合示例:FK、M2M、O2O 关系与列表模型的序列化 |
| tutorial_1.py | 教程一:基础用法 |
| tutorial_2.py | 教程二:Queryset 与列表 |
| tutorial_3.py | 教程三:关系与 Early-init |
| tutorial_4.py | 教程四:PydanticMeta 与 Callables |
| early_init.py | 演示提前生成模型时关系缺失,以及init_models之后关系补齐 |
| computed_fields.py | 计算字段与可空外键的完整行为演示 |
| recursive.py | 自引用递归模型(allow_cycles=True+max_recursion=4)的序列化 |
运行方式(示例均使用内存 SQLite,无需额外配置):
python examples/pydantic/tutorial_1.py python examples/pydantic/tutorial_2.py python examples/pydantic/tutorial_3.py python examples/pydantic/tutorial_4.py python examples/pydantic/basic.py python examples/pydantic/early_init.py python examples/pydantic/computed_fields.py python examples/pydantic/recursive.py对应的单元测试集中在 tests/contrib/test_pydantic.py(2300 余行),覆盖了 Schema 精确断言(如 test_event_schema)、列表 Schema(test_eventlist_schema)、循环引用序列化(test_cycle_serialisation)、计算字段(test_computed_field)以及计算字段在“关系被排除且未预取”时的NoValuesFetched传播(test_computed_field_excluded_relation_not_prefetched)等场景,可作为行为契约参考。
已知边界与注意事项
- 仅序列化:插件当前不支持反序列化(不能将 JSON 直接还原为 Tortoise Model);
- Early-init 前置条件:若在
Tortoise.init之前生成模型,必须先用Tortoise.init_models(["模块名"], "models")初始化模型结构,否则关系字段不会出现在 Schema 中(init_models不会初始化任何数据库结构); - 计算字段必须是同步方法:序列化器无法调用异步方法;访问关系数据依赖序列化前的自动预取,建议在计算字段内部捕获
NoValuesFetched做兜底; - 计算字段必须标注返回类型:没有
-> Type注解的方法不会进入生成的 Schema; - 循环引用有风险:
allow_cycles=True可能生成海量嵌套数据,务必配合exclude/include与合理的max_recursion; - Schema API 以当前依赖的 Pydantic 版本为准:当前仓库基于 Pydantic v2,推荐使用
.model_json_schema()/.model_dump_json()等新版 API(旧文档中的.schema()为 Pydantic v1 时代接口)。
以上内容均以当前仓库的 docs/contrib/pydantic.rst、tortoise/contrib/pydantic/ 源码及 examples/pydantic/ 示例为依据,可直接在本地运行验证。
- 数据库
- 后端
【免费下载链接】tortoise-orm
Familiar asyncio ORM for python, built with relations in mind
相关推荐
Tortoise ORM与Pydantic结合:数据验证与序列化的终极指南
Tortoise ORM与Pydantic结合:数据验证与序列化的终极指南 在Python异步编程的世界中,Tortoise ORM和Pydantic是两个不可
数据库后端Tortoise-ORM与Pydantic集成教程:模型转换与序列化实践
Tortoise ORM与Pydantic集成教程:模型转换与序列化实践 前言 在现代Python开发中,ORM框架和模型验证库是两个非常重要的组件。Torto
数据库后端Tortoise-ORM 结合 Pydantic 实现模型关系与序列化教程
Tortoise ORM 结合 Pydantic 实现模型关系与序列化教程 前言 在现代Python Web开发中,ORM(对象关系映射)和数据验证/序列化是两
数据库后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考