news 2026/9/16 18:22:30

Litestar DTO 教程:用 DTO 工厂构建灵活的数据传输层

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Litestar DTO 教程:用 DTO 工厂构建灵活的数据传输层

Litestar DTO 教程:用 DTO 工厂构建灵活的数据传输层

【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar

本篇为 Litestar 官方 DTO 教程(Data Transfer Object Tutorial)的中文技术详解。教程面向已熟悉 Litestar 路由处理器(route handler)基础用法的开发者,完整覆盖从"返回 dataclass 的朴素模式"到"基于 DTO 的接收、校验、隐藏、重命名与分层复用"的全过程。读完本篇,你将掌握DataclassDTODTOConfigDTOData三个核心工具的实际用法,能独立用 DTO 工厂实现响应字段裁剪、请求数据校验、只读字段、PUT/PATCH 更新以及 Controller 分层 DTO 声明等实战能力。

前置要求与阅读路径

本教程假设你已经熟悉 Litestar 及路由处理器等基础概念。如果尚未接触,建议先阅读仓库中的 TODO 应用基础教程(Developing a basic TODO application)后再回到本篇。

教程的全部可运行示例代码位于仓库的docs/examples/data_transfer_objects/factory/tutorial/目录下,每节内容都有对应的独立脚本;核心 API 的参考文档可查阅 docs/reference/dto。


1. 起步:返回 dataclass 的朴素模式

我们从一个最简单的应用开始。定义一个名为Person的 Pythondataclass数据模型,包含nameageemail三个属性;再定义一个路径为/person/{name:str}的 GET 路由处理器:路径中的{name:str}表示名为name的字符串类型路径参数。最后创建Litestar应用实例并注册该路由处理器。

完整代码见 initial_pattern.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, get from litestar.params import FromPath @dataclass class Person: name: str age: int email: str @get("/person/{name:str}", sync_to_thread=False) def get_person(name: FromPath[str]) -> Person: # Your logic to retrieve the person goes here # For demonstration purposes, a placeholder Person instance is returned return Person(name=name, age=30, email=f"email_of_{name}@example.com") app = Litestar(route_handlers=[get_person])

在 Litestar 中,这种模式是"开箱即用"的:从路由处理器返回 dataclass 实例是原生支持的行为。Litestar 会自动将该 dataclass 实例序列化为可通过网络传输的bytes(默认输出为 JSON)。

将上面的脚本保存为app.py,使用litestar run命令启动,然后访问http://localhost:8000/person/peter,浏览器中会看到类似下面的输出:

可以看到,返回的 JSON 中包含了nameageemail全部字段。代码中的FromPath[str]用于声明路径参数并完成类型转换,sync_to_thread=False则让同步处理器直接在线程内执行(避免同步函数被调度到线程池,适用于本例这类纯计算场景)。

不过,真实世界的应用很少这么简单。例如我们可能希望在用户创建之后,限制对外暴露的信息——比如把用户的邮箱从响应中隐藏掉。这正是 Data Transfer Object(数据传输对象)要解决的问题。


2. 第一个 DTO:隐藏 email 字段

我们在脚本中引入一个 DTO 类ReadDTO,将其配置为排除Person.email字段,并让路由处理器使用该 DTO 处理响应。完整代码见 simple_dto_exclude.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, get from litestar.dto import DataclassDTO, DTOConfig from litestar.params import FromPath @dataclass class Person: name: str age: int email: str class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) @get("/person/{name:str}", return_dto=ReadDTO, sync_to_thread=False) def get_person(name: FromPath[str]) -> Person: # Your logic to retrieve the person goes here # For demonstration purposes, a placeholder Person instance is returned return Person(name=name, age=30, email=f"email_of_{name}@example.com") app = Litestar(route_handlers=[get_person])

本节的改动集中在三处:

  1. 新增两个导入DTOConfigDataclassDTO(均从litestar.dto导入)。
  2. 定义 DTO 类
    • DTOConfig 用于配置 DTO。本例使用exclude={"email"}排除字段,此外它还有大量其他配置选项,本教程后续会逐一覆盖。
    • DataclassDTO 是一个专门从 dataclass 生成 DTO 的工厂类,同时它是一个typing.Generic泛型类,接受类型参数。当我们提供类型参数时,该类就变成泛型类的一个特化版本:DataclassDTO[Person]即"专门在Person实例与传输数据之间进行转换的 DTO 类型"。
  3. 路由处理器启用 DTO:通过return_dto=ReadDTO让 DTO 负责处理处理器返回值到响应数据的转换。

注意:并不必须通过子类化DataclassDTO来创建特化 DTO,例如ReadDTO = DataclassDTO[Person]同样是合法的特化 DTO。但子类化允许我们挂载配置对象(config = DTOConfig(...)),同时完成类型特化,因此教程采用子类方式。

再次访问http://localhost:8000/person/peter,响应中不再包含email字段:

至此,我们已经成功隐藏了用户的邮箱地址。


3. 排除嵌套模型的字段:点分路径语法

exclude选项不仅支持顶层字段,还支持通过**点分路径(dotted paths)**定位到嵌套模型中的字段。例如exclude={"a.b"}将排除嵌套在a属性上的实例的b属性。

我们为模型增加一个与Person关联的Address模型。完整代码见 nested_exclude.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, get from litestar.dto import DataclassDTO, DTOConfig from litestar.params import FromPath @dataclass class Address: street: str city: str country: str @dataclass class Person: name: str age: int email: str address: Address class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email", "address.street"}) @get("/person/{name:str}", return_dto=ReadDTO, sync_to_thread=False) def get_person(name: FromPath[str]) -> Person: # Your logic to retrieve the person goes here # For demonstration purposes, a placeholder Person instance is returned address = Address(street="123 Main St", city="Cityville", country="Countryland") return Person(name=name, age=30, email=f"email_of_{name}@example.com", address=address) app = Litestar(route_handlers=[get_person])

Address模型有三个属性:streetcitycountryPerson模型新增了address属性。ReadDTO使用点分路径"address.street"排除了嵌套Address模型中的street字段。

调用处理器后可以看到,响应中address对象保留了citycountry,但street不再出现:


4. 排除集合中嵌套模型的字段:类型参数索引语法

在 Python 中,泛型类型可以接受一个或多个类型参数(方括号中的类型),典型场景是表示某种类型的集合,例如List[Person]List是泛型容器类型,Person特化了集合中元素的类型。

对于一个拥有任意数量类型参数的泛型类型,例如GenericType[Type0, Type1, ..., TypeN],我们使用类型参数的索引来指明排除操作针对的是哪个类型:

  • a.0.b:排除a的第一个类型参数(Type0)中实例的b字段;
  • a.1.b:排除a的第二个类型参数(Type1)中实例的b字段;
  • 依此类推。

下面我们为Person模型增加一个自引用(self-referencing)的children关系。完整代码见 nested_collection_exclude.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, get from litestar.dto import DataclassDTO, DTOConfig from litestar.params import FromPath @dataclass class Address: street: str city: str country: str @dataclass class Person: name: str age: int email: str address: Address children: list[Person] class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email", "address.street", "children.0.email", "children.0.address"}) @get("/person/{name:str}", return_dto=ReadDTO, sync_to_thread=False) def get_person(name: FromPath[str]) -> Person: # Your logic to retrieve the person goes here # For demonstration purposes, a placeholder Person instance is returned address = Address(street="123 Main St", city="Cityville", country="Countryland") child1 = Person(name="Child1", age=10, email="child1@example.com", address=address, children=[]) child2 = Person(name="Child2", age=8, email="child2@example.com", address=address, children=[]) return Person( name=name, age=30, email=f"email_of_{name}@example.com", address=address, children=[child1, child2], ) app = Litestar(route_handlers=[get_person])

现在一个Person可以有多个children,每个 child 又可以有多个 children,层层嵌套。我们通过"children.0.email""children.0.address"显式排除了所有子Personemailaddress字段(childrenlist[Person],其第 0 个类型参数即Person)。

处理器中为Person增加了两个 child,且每个 child 自身没有 children。输出如下:

子对象成功出现在响应中,且它们的 email 与 address 都被排除。细心的读者可能会注意到:我们并没有显式排除Person.childrenchildren字段(例如children.0.children),但该字段并未出现在输出中。要理解原因,我们来看下一节:max_nested_depth配置。


5. max_nested_depth:控制嵌套深度

上一节的现象是:即便没有显式排除children.0.children,每个嵌套Personchildren集合也没有出现在响应里。按照"未排除即应输出"的直觉,children集合中的每个Person应该有一个空的children集合——但事实并非如此。原因正是 DTOConfig.max_nested_depth 及其默认值1

max_nested_depth用于限制响应中包含的嵌套对象深度。在本例中:

  • Person有一个children集合,集合元素是嵌套的Person对象——这算作 1 层嵌套深度;
  • Person.children中每个元素的children集合则处于第 2 层嵌套,因此被max_nested_depth的默认值1排除掉了。

下面修改脚本,把max_nested_depth提升到2,让孙级 children 也出现在响应中。完整代码见 max_nested_depth.py,核心改动只有一处——DTO 配置:

class ReadDTO(DataclassDTO[Person]): config = DTOConfig( exclude={"email", "address.street", "children.0.email", "children.0.address"}, max_nested_depth=2, )

现在输出中可以看到那些空集合("children": []):

本教程后续章节将恢复使用默认值1

从源码看,max_nested_depth的默认值定义在 config.py 的DTOConfig数据类中(max_nested_depth: int = 1),它的作用是在 DTO 后端生成传输模型时限制递归展开的层数,既避免无限自引用模型导致递归失控,也天然防止深层嵌套对象被意外暴露。


6. 字段重命名:显式声明与重命名策略

字段在序列化时的名称可以通过两种方式改变:显式声明新名称,或声明重命名策略

6.1 显式重命名:rename_fields

我们可以通过DTOConfig.rename_fields属性显式重命名字段。它是一个字典,键为原始字段名,值为新字段名。

下面的例子把address字段重命名为location(完整代码见 explicit_field_renaming.py):

class ReadDTO(DataclassDTO[Person]): config = DTOConfig( exclude={"email", "address.street", "children.0.email", "children.0.address"}, rename_fields={"address": "location"}, )

响应中的address字段被重命名为location

6.2 重命名策略:rename_strategy

除了逐字段显式重命名,还可以使用字段重命名策略。策略通过DTOConfig.rename_strategy配置指定。

Litestar 内置支持以下策略:

策略说明
lower将字段名转换为小写
upper将字段名转换为大写
camel将字段名转换为驼峰式(camel case)
pascal将字段名转换为帕斯卡式(pascal case)

提示:也可以自定义策略——向rename_strategy传入一个"接收字段名并返回新字段名"的可调用对象即可。

把示例改为使用upper策略(完整代码见 field_renaming_strategy.py):

class ReadDTO(DataclassDTO[Person]): config = DTOConfig( exclude={"email", "address.street", "children.0.email", "children.0.address"}, rename_strategy="upper", )

结果中所有字段名都被转换为大写:

从 config.py 的源码注释可以确认:rename_fields中显式声明的字段不受rename_strategy影响("Fields defined inrename_fieldsare ignored"),两者可以同时使用、各司其职。


7. 接收数据:从客户端控制入站数据

到目前为止,我们只处理了"返回数据"这一半。另一半是:控制从客户端接收的数据

为了简化演示,我们把数据模型缩减回只有nameageemail三个属性的Person。完整代码见 simple_receiving_data.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, post from litestar.dto import DataclassDTO, DTOConfig @dataclass class Person: name: str age: int email: str class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) @post("/person", return_dto=ReadDTO, sync_to_thread=False) def create_person(data: Person) -> Person: # Logic for persisting the person goes here return data app = Litestar(route_handlers=[create_person])

这里的要点:

  • 与之前一样,ReadDTO通过return_dto配置给处理器,负责排除返回负载中的email字段;
  • 处理器变成了 @post() 处理器,其函数签名同时声明了接受和返回Person实例;
  • Litestar 原生支持将请求负载解码为 Python dataclass,所以本例即使不为入站数据定义 DTO 也能正常工作——入站方向的 DTO 是可选的。

现在需要向服务器发送数据来测试程序,可以使用 Postman 之类的工具,或 Posting。下面是一个请求/响应负载示例:


8. 只读字段:客户端永远不该提交的字段

有些字段永远不应由客户端指定。例如创建新资源实例时,模型的id字段应该由服务端生成,而不是由客户端提交。

下面我们给Person模型加上id字段,并新建一个忽略idWriteDTO。完整代码见 read_only_fields_error.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, post from litestar.dto import DataclassDTO, DTOConfig @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class WriteDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}) @post("/person", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False) def create_person(data: Person) -> Person: # Logic for persisting the person goes here return data app = Litestar(route_handlers=[create_person])

关键点:

  • WriteDTO被指示忽略id属性;
  • WriteDTO通过dto=WriteDTO关键字参数分配给处理器,这意味着创建新Person实例时,从客户端接收的任何数据中的id字段都会被忽略

当我们试图携带id字段创建新Person实例时,会得到一个错误:

发生了什么?DTO 试图构造Person模型实例,但我们已经把id字段从接受的客户端数据中排除了。而idPerson模型必填字段,模型构造函数因此抛错。

解决这个问题有不止一种方式,例如:

  • id字段一个默认值,并在处理器中覆盖默认值;
  • 创建一个完全没有id字段的独立模型,在处理器中把数据从该模型转移到Person模型。

不过,Litestar 内置了更优雅的方案:DTOData


9. DTOData:延迟实例化与数据访问

有些时候,数据不应当被立即解析成目标类的实例。上一节正是这样的例子:当必填字段被客户端数据排除或缺失时,立即实例化类必然报错。解决方案就是DTOData类型。

完整代码见 dto_data.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, post from litestar.dto import DataclassDTO, DTOConfig, DTOData @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class WriteDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}) @post("/person", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False) def create_person(data: DTOData[Person]) -> Person: # Logic for persisting the person goes here return data.create_instance(id=1) app = Litestar(route_handlers=[create_person])

要点拆解:

  • DTOData是一个数据容器,既可以用于创建目标类实例,也可以访问底层已解析、已校验的数据。本例从litestar.dto导入它;
  • 处理器的数据参数类型从Person改为DTOData[Person],相应地,注入到函数中的"入站客户端数据"将是一个DTOData实例;
  • 在处理器内部,我们为id字段生成一个值,然后通过DTOData.create_instance()方法创建Person实例。从源码(data_structures.py)可见,create_instance(**kwargs)会先把 DTO 校验后的数据(self._data_as_builtins)拷贝为字典,再用传入的 kwargs 覆盖对应键,最后交给 DTO 后端转换为目标类型实例——kwargs 的优先级高于 DTO 校验数据

应用恢复到正常工作状态:

技巧:要为嵌套属性提供值,可以使用"双下划线"语法作为create_instance()的关键字参数。例如address__id=1会设置所创建实例的address属性的id。这一机制在源码中由_set_nested_dict_value()实现——它按__切分键名并递归写入嵌套字典。

DTOData还有其他实用的方法,我们将在下一节(更新实例)中看到。


10. 更新实例:PUT 与 PATCH

本节展示如何使用DTOData更新已存在的实例。

10.1 PUT 处理器:全量替换语义

PUT 请求的特征是:要求提交完整的数据模型才能进行更新。

完整代码见 put_handlers.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, put from litestar.dto import DataclassDTO, DTOConfig, DTOData from litestar.params import FromPath @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class WriteDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}) @put("/person/{person_id:int}", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False) def update_person(person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) app = Litestar(route_handlers=[update_person])

要点:

  • 脚本定义了一个路径为/person/{person_id:int}的 PUT 处理器,路由参数person_id指明要更新哪个 person;
  • 处理器中先创建一个Person实例(模拟数据库查询结果),然后把它传给DTOData.update_instance()方法;该方法返回被修改后的同一个实例

从源码(data_structures.py)看,update_instance(instance, **kwargs)的逻辑是:把 DTO 校验数据与 kwargs 合并,然后对每个键执行setattr(instance, k, v),原地修改并返回实例。

调用效果:

10.2 PATCH 处理器:部分更新语义

与 PUT 要求提交整个数据模型不同,PATCH 请求允许只提交数据模型属性的任意子集来进行更新。

完整代码见 patch_handlers.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, patch from litestar.dto import DataclassDTO, DTOConfig, DTOData from litestar.params import FromPath @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class PatchDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}, partial=True) @patch("/person/{person_id:int}", dto=PatchDTO, return_dto=ReadDTO, sync_to_thread=False) def update_person(person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) app = Litestar(route_handlers=[update_person])

改动要点:

  • 处理器从@put改为 @patch() 处理器;
  • 引入PatchDTO类:配置与WriteDTO类似(排除id),但额外设置了partial=True。该设置允许对资源进行部分更新——即客户端提交的字段中缺失的属性将被视为"不更新",而不是"置空或报错"。

演示效果:


11. 在应用分层上声明 DTO:从处理器到 Controller

到目前为止,DTO 都是逐个处理器声明的。真实应用往往有多个处理器,让我们先看一个声明了多个处理器的脚本。完整代码见 multiple_handlers.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Litestar, patch, post, put from litestar.dto import DataclassDTO, DTOConfig, DTOData from litestar.params import FromPath @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class WriteDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}) class PatchDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}, partial=True) @post("/person", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False) def create_person(data: DTOData[Person]) -> Person: # Logic for persisting the person goes here return data.create_instance(id=1) @put("/person/{person_id:int}", dto=WriteDTO, return_dto=ReadDTO, sync_to_thread=False) def update_person(person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) @patch("/person/{person_id:int}", dto=PatchDTO, return_dto=ReadDTO, sync_to_thread=False) def patch_person(person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) app = Litestar(route_handlers=[create_person, update_person, patch_person])

可以看到,dto=WriteDTO, return_dto=ReadDTO在三个处理器上重复出现。DTO 可以定义在应用的任何分层(layer)上,这给了我们整理代码的机会——把处理器搬进一个 Controller,并在 Controller 层定义 DTO。完整代码见 controller.py:

from __future__ import annotations from dataclasses import dataclass from litestar import Controller, Litestar, patch, post, put from litestar.dto import DataclassDTO, DTOConfig, DTOData from litestar.params import FromPath @dataclass class Person: name: str age: int email: str id: int class ReadDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"email"}) class WriteDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}) class PatchDTO(DataclassDTO[Person]): config = DTOConfig(exclude={"id"}, partial=True) class PersonController(Controller): dto = WriteDTO return_dto = ReadDTO @post("/person", sync_to_thread=False) def create_person(self, data: DTOData[Person]) -> Person: # Logic for persisting the person goes here return data.create_instance(id=1) @put("/person/{person_id:int}", sync_to_thread=False) def update_person(self, person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) @patch("/person/{person_id:int}", dto=PatchDTO, sync_to_thread=False) def patch_person(self, person_id: FromPath[int], data: DTOData[Person]) -> Person: # Usually the Person would be retrieved from a database person = Person(id=person_id, name="John", age=50, email="email_of_john@example.com") return data.update_instance(person) app = Litestar(route_handlers=[PersonController])

对比可以看出:

  • 之前的脚本为每条路由定义独立的处理器函数,新脚本把这些路由组织进PersonController类,从而把公共配置上移到 Controller 层;
  • PersonController类上同时定义了dto = WriteDTOreturn_dto = ReadDTO,无需再在每个处理器上重复声明;
  • 我们仍然在patch_person处理器上直接定义dto=PatchDTO,用于覆盖 Controller 层级的dto设置——这正是 Litestar 分层配置的覆盖机制:越内层的声明优先级越高。

12. DTOConfig 全参数一览(源码级)

本教程使用的所有配置都来自 DTOConfig。结合源码,完整的可配置项如下:

配置项默认值说明
exclude: set[str]set()显式排除字段。字段名为点分路径(如"address.street")。指定exclude时,未列出的字段默认包含。与include互斥,同时指定会抛出ImproperlyConfiguredException
include: set[str]set()显式包含字段(白名单模式)。指定include时,未列出的字段默认排除。与exclude互斥
rename_fields: dict[str, str]dict()字段名到新名称的映射,用于显式重命名
rename_strategyNone重命名策略:内置upperlowercamelpascal,或传入自定义可调用对象。rename_fields中声明的字段不受其影响
max_nested_depth: int1允许数据传输的嵌套最大深度,用于防止深层/递归模型被无限展开
partial: boolFalse是否允许传输部分数据(PATCH 语义)
underscore_fields_private: boolTrue以下划线开头的字段视为私有字段,默认排除在数据传输之外
experimental_codegen_backend: bool \| NoneNone是否启用实验性代码生成后端
forbid_unknown_fields: boolFalse原始数据中出现模型未定义的字段时是否抛出异常

excludeinclude的互斥校验在DTOConfig.__post_init__中强制执行(见 config.py),这一点从源码可以直接确认。


深入阅读

  • 完整示例代码目录:docs/examples/data_transfer_objects/factory/tutorial
  • DTO 工厂更多用法(白名单include、私有字段underscore_fields_private、未知字段处理等):docs/examples/data_transfer_objects/factory
  • API 参考:DTO 配置、DTO 数据结构、DataclassDTO、DTO 基类
  • 核心源码:litestar/dto/config.py、litestar/dto/data_structures.py、litestar/dto/dataclass_dto.py
  • 单元测试(可验证create_instance的双下划线嵌套参数等行为):tests/unit/test_dto/test_factory/test_integration.py、tests/unit/test_dto/test_config.py
  • DTO 的进阶使用(DTO 与请求/响应集成、自定义类型):docs/usage/dto

【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 18:21:45

I3C协议调试实战:动态地址分配与IBI中断如何用专业分析仪定位

早几年调I2C设备的时候,逻辑分析仪一挂,波形一抓,基本就能定位个八九不离十。到了I3C这个协议上,这招不太好使了。动态地址、IBI中断、热加入这些特性,都是I2C时代没有的,普通分析仪抓回来一堆乱码&#xf…

作者头像 李华
网站建设 2026/9/16 18:21:16

四大厂商光模块光功率查看命令与阈值解读

1. 光模块光功率查看:为什么这事儿值得花一整篇讲清楚?在机房巡检、割接前检查、故障排查甚至日常值班时,我最常被喊去干的一件事就是:“张工,快看看这个口光衰多少?”——不是看设备有没有亮,而…

作者头像 李华
网站建设 2026/9/16 18:17:26

ECharts词云图配置全解析:从核心参数到实战优化

简介:面向前端开发者的ECharts词云图实战资料包,围绕词云图从数据准备、图表初始化到常用配置项设定给出完整demo,并逐项讲解sizeRange、rotationRange、textRotation、textStyle等核心参数,帮助读者快速做出适配自身项目的词云效…

作者头像 李华