Core API 模式
构建 REST 响应——或任何用例 DTO——享受与 GraphQL 模式相同的 DataLoader 批量加载和 N+1 预防。
核心概念按顺序递进:隐式自动加载 → resolve_* → post_* → 跨层数据流。
第 1 步:DefineSubset + 隐式自动加载
最简单的 Core API 用法:从 SQLModel 实体选择字段,声明关系字段,它们自动加载。
from sqlmodel import SQLModel, select
from nexusx import DefineSubset, ErManager
class UserDTO(DefineSubset):
__subset__ = (User, ("id", "name"))
class TaskDTO(DefineSubset):
__subset__ = (Task, ("id", "title", "owner_id"))
owner: UserDTO | None = None # 名称匹配 Task.owner 关系 → 自动加载
class SprintDTO(DefineSubset):
__subset__ = (Sprint, ("id", "name"))
tasks: list[TaskDTO] = [] # 名称匹配 Sprint.tasks 关系 → 自动加载
Tip
心智模型很简单:DefineSubset 选择要暴露哪些字段,Resolver 自动填充关系字段。
ErManager 初始化
# 应用启动时——执行一次
er = ErManager(base=SQLModel, session_factory=async_session)
Resolver = er.create_resolver()
ErManager发现所有 SQLModel 实体及其 ORM 关系create_resolver()返回一个绑定了实体图的 Resolver 类
Warning
base 和 entities 参数是互斥的——你不能同时传两个。
在 FastAPI 中使用
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/sprints")
async def get_sprints():
async with async_session() as session:
sprints = (await session.exec(select(Sprint))).all()
dtos = [SprintDTO(id=s.id, name=s.name) for s in sprints]
return await Resolver().resolve(dtos)
隐式自动加载的四个条件
当以下四个条件全部满足时,Resolver 自动加载关系字段(无需手写 resolve_*):
- 字段没有对应的
resolve_*方法 - 字段是额外字段(不在
__subset__定义中) - 字段名匹配已注册的 ORM/自定义关系
- 字段类型是 BaseModel DTO 且与关系目标实体兼容
DefineSubset 规则
__subset__接受元组(Entity, ('field1', 'field2'))- FK 字段(如
owner_id)自动从序列化输出中隐藏,但内部仍可在resolve_*中访问 - 关系字段声明在类体中(非
__subset__),类型必须是 DTO 类型
工作原理
SprintDTO(id=1, name="Sprint 1")
→ 隐式自动加载: tasks → [TaskDTO(...), TaskDTO(...)]
→ 每个 TaskDTO: 隐式自动加载: owner → UserDTO(...)
→ 结果:完整的嵌套响应树
每个关系只执行一次 DataLoader 查询,无论有多少个 Sprint 或 Task。
DTO 类型约束
# 错误——禁止直接使用 SQLModel 实体
class TaskDTO(DefineSubset):
owner: User | None = None # TypeError!
# 正确——使用 DTO 类型
class TaskDTO(DefineSubset):
owner: UserDTO | None = None # OK
Warning
关系字段的类型必须是 DTO 类型(DefineSubset 或 BaseModel 的子类)。直接使用 SQLModel 实体类型会抛出 TypeError。
回顾
DefineSubset从 SQLModel 实体生成 DTO——选择字段,隐藏 FK- 隐式自动加载在字段名匹配已注册关系时自动填充关系字段
ErManager发现实体并创建 Resolver- 关系字段类型必须是 DTO 类型,不能是 SQLModel 实体
下一步
- Core API 进阶 —
resolve_*/post_*/ 跨层数据流 - 自定义关系 — 非 ORM 关系声明