ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Litestar DTO 嵌套集合排除实战:用索引点分路径精确控制集合元素字段

Litestar DTO 嵌套集合排除实战:用索引点分路径精确控制集合元素字段 Litestar DTO 嵌套集合排除实战用索引点分路径精确控制集合元素字段【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar在 Litestar 中Data Transfer ObjectDTO工厂通过DTOConfig.exclude的点分路径dotted path语法控制序列化输出而面对list[Person]这类泛型集合时路径语法需要额外引入“类型参数索引”维度。本文以官方 DTO 教程第三篇为核心结合源码讲解如何对嵌套集合中的每个元素按类型参数下标精确排除字段如children.0.email并解释为何未显式排除的深层字段如children.0.children仍不会出现在响应中——这背后是DTOConfig.max_nested_depth的默认深度限制。读完本文你将掌握泛型集合 DTO 排除规则、索引路径的书写方式以及嵌套深度控制机制的底层原理。一、背景泛型类型与类型参数索引在 Python 中泛型类型generic type可以接受一个或多个类型参数即方括号内的类型。这一模式最常见的场景是表示“某种类型的集合”例如List[Person]其中List是泛型容器类型Person则将集合的内容特化为只包含Person类的实例。Litestar 的 DTO 排除路径正是建立在类型参数的概念之上。对于一个具有任意数量类型参数的泛型类型例如GenericType[Type0, Type1, ..., TypeN]DTO 路径语法使用**类型参数的索引下标**来指明排除操作所指向的类型。具体规则为a.0.b排除a的第一个类型参数中的b字段a.1.b排除a的第二个类型参数中的b字段依此类推a.N.b对应第N1个类型参数。换言之当被排除的目标字段位于一个泛型集合内部时路径中会插入一个数字段用该数字指定“集合元素的类型参数下标”。对于list[Person]这种单参数泛型下标固定为0因此路径写作children.0.field。本教程是官方 DTO 教程系列的第三篇前序基础见 01-simple-dto-exclude.rst第一个 DTO、单字段排除与 02-nested-exclude.rst嵌套模型的点分路径排除如address.street。本文在其基础上引入“集合内元素的嵌套排除”。二、模型为 Person 添加自引用 children 关系为了演示集合内的字段排除我们在Person模型上添加一个自引用的children关系。完整示例代码位于 nested_collection_exclude.pyfrom __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_dtoReadDTO, sync_to_threadFalse) 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(street123 Main St, cityCityville, countryCountryland) child1 Person(nameChild1, age10, emailchild1example.com, addressaddress, children[]) child2 Person(nameChild2, age8, emailchild2example.com, addressaddress, children[]) return Person( namename, age30, emailfemail_of_{name}example.com, addressaddress, children[child1, child2], ) app Litestar(route_handlers[get_person])关键点解读children: list[Person]使Person产生自引用——一个Person可以拥有一个或多个children而每个 child 又可以拥有自己的children依此类推形成任意深度的树状结构。由于使用了from __future__ import annotations自引用类型注解可以安全书写。address: Address是上一节02-nested-exclude.rst引入的嵌套模型包含street、city、country三个字段。三、DTO 配置用索引路径排除集合元素的字段ReadDTO继承了DataclassDTO[Person]并在DTOConfig中声明了四类排除规则可逐条拆解config DTOConfig(exclude{email, address.street, children.0.email, children.0.address})排除路径含义email排除顶层Person.email字段与第一篇教程相同address.street排除嵌套Address模型中的street字段第二篇教程的点分路径语法children.0.email排除children集合中每个元素类型参数下标 0即Person的email字段children.0.address排除children集合中每个元素的整个address嵌套模型这里正是本文的核心语法children.0.email中的数字0不是数组下标即不是“排除第一个 child 的 email”而是类型参数索引表示“对children集合中所有元素的Person类型统一应用该排除规则”。因此所有 child 的email与address都会被排除而非仅针对索引为 0 的那一个元素。从源码看该规则在 DTO 后端按“逐段消费路径前缀”的方式实现。litestar/dto/_backend.py中的_filter_nested_fieldlitestar/dto/_backend.py#L565-L567会以当前字段名为前缀剥离children.0.email的第一段把剩余的email传给下一层模型继续解析def _filter_nested_field(field_name_set: Set[str], field_name: str) - Set[str]: Filter a nested field name. return {split[1] for s in field_name_set if (split : s.split(., 1))[0] field_name and len(split) 1}可见children.0.email在children这一层被识别为前缀children剩余0.email进入list[Person]的元素类型解析0段对应类型参数下标 0即元素类型Personemail段最终在Person的字段层面生效。这与文档描述的“用类型参数索引指明排除目标类型”完全吻合。四、处理程序与运行验证在get_person处理程序中通过路径参数nameFromPath[str]接收人名构造一个共享的Address实例创建两个 childChild110 岁与Child28 岁它们自身没有children空列表[]最终返回一个包含这两个 child 的Person。路由get(/person/{name:str}, return_dtoReadDTO, sync_to_threadFalse)通过return_dtoReadDTO指定响应使用ReadDTO进行序列化sync_to_threadFalse表示处理函数为同步函数且无需提交线程池执行纯演示用途。将上述脚本保存为app.py运行litestar run然后访问http://localhost:8000/person/peter即可观察响应结果。五、输出分析集合元素字段已按预期排除接口返回的 JSON 响应如下对应文档配图 nested_collection_exclude.png{ name: peter, age: 30, address: { city: Cityville, country: Countryland }, children: [ {name: Child1, age: 10}, {name: Child2, age: 8} ] }逐项核对排除效果顶层email未出现——email生效address中只剩city与countrystreet被排除——address.street生效两个 child 均只保留name与ageemail与整个address都被排除——children.0.email与children.0.address对集合所有元素生效印证了“下标指类型参数而非数组下标”的结论。这一行为在仓库测试中被固化验证。参见 tests/examples/test_dto/test_tutorial.py#L33-L44 中的test_nested_collection_excludedef test_nested_collection_exclude(): from docs.examples.data_transfer_objects.factory.tutorial.nested_collection_exclude import app with TestClient(appapp) as client: response client.get(/person/peter) assert response.status_code 200 assert response.json() { name: peter, age: 30, address: {city: Cityville, country: Countryland}, children: [{name: Child1, age: 10}, {name: Child2, age: 8}], }测试使用TestClient直接断言响应 JSON与浏览器截图展示的结构完全一致。六、一个值得注意的现象未排除的 children.0.children 为何消失细心的读者会发现我们在DTOConfig中没有排除children.0.children即 child 自身的children字段但在上面的输出中Child1和Child2都没有children键。为什么答案在于DTOConfig.max_nested_depth配置项及其默认值。Person的顶层对象拥有children集合集合中的元素是嵌套的Person对象——这构成第 1 层嵌套而Person.children集合中每个元素child若再拥有children集合则处于第 2 层嵌套。max_nested_depth的默认值为1见 litestar/dto/config.py#L51 的max_nested_depth: int 1因此第 2 层及更深的嵌套字段会被自动截断不进入序列化输出。从源码看深度限制在 DTO 后端解析模型时强制生效。litestar/dto/_backend.py中每当检测到嵌套字段detect_nested_field且当前嵌套深度等于max_nested_depth时会直接抛出RecursionError终止继续下钻litestar/dto/_backend.py#L424-L426if self.dto_factory.detect_nested_field(field_definition): if nested_depth self.dto_factory.config.max_nested_depth: raise RecursionError unique_name f{unique_name}{field_definition.raw.__name__} nested_field_definitions self.parse_model( model_typefield_definition.annotation, excludeexclude, includeinclude, rename_fieldsrename_fields, nested_depthnested_depth 1, )也就是说children集合里的Person元素在第 1 层被正常解析因此 child 的name、age得以保留但当解析 child 自身的children字段时嵌套深度已达上限等于默认值 1该字段被判定为超出允许深度而排除。这正是“未显式排除却未出现在输出中”的根本原因。关于max_nested_depth的详细用法如将其调整为2以输出 child 的空children集合请参见教程系列的下一篇 04-max-nested-depth.rst该文档对应的测试用例test_max_nested_depthtests/examples/test_dto/test_tutorial.py#L47-L58断言了children元素携带空children: []的输出形态可作对照验证。七、小结与进阶提示核心要点回顾集合内排除使用类型参数索引对list[T]、dict[str, T]等泛型集合路径中插入数字段指定类型参数下标如children.0.email作用于集合内所有元素而非单个数组下标。路径可多段组合email、address.street、children.0.email、children.0.address等规则可在同一DTOConfig.exclude中自由组合逐段递进解析。深度限制兜底即便不显式排除max_nested_depth默认 1也会截断过深嵌套避免响应体无限膨胀。进阶注意事项DTOConfig中exclude与include互斥同时指定会抛出ImproperlyConfiguredExceptionlitestar/dto/config.py#L62-L66include集合同样支持索引点分路径语法可用于“仅保留集合元素中的若干字段”的白名单场景对于更深的数据结构可通过提高max_nested_depth放开嵌套限制但需权衡响应体大小与信息暴露面。至此你已经掌握 Litestar DTO 对“嵌套集合内字段”的精确排除能力。下一步建议阅读 04-max-nested-depth.rst 深入理解深度控制或继续本系列后续章节05-renaming-fields.rst 起的字段重命名、数据接收、只读字段与分层 DTO 声明系统构建对 DTO 的完整认知。【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表