Skip to content

065. SQLModel 预加载、去重与关系属性 ​

学习目标 ​

这一节继续讲 SQLModel 的关系查询。

学完后,你应该能理解:

  • ORM 里的关系定义和数据库外键有什么区别;
  • joinedload 预加载会生成什么 SQL;
  • 为什么一对多查询后要 unique 去重;
  • 未预加载时访问关系属性为什么会报错;
  • 如何用计算属性抹平访问差异;
  • 审批单和项目如何建立关系。

关系定义不是数据库外键 ​

模型类里可以定义关系属性。

例如项目模型里定义负责人:

py
leader

这表示 ORM 查询时如何把项目和用户关联起来。

它不是数据库层面的外键约束。

数据库外键会影响写入、删除、分库分表和依赖关系处理。

课程项目一般不在数据库层面使用外键,而是在后端代码里控制业务关系。

关系定义主要服务查询 ​

ORM 的关系定义主要帮助查询。

它告诉 ORM:

  • 哪个字段关联哪张表;
  • 查出来的数据放到哪个属性;
  • 两个模型之间是一对一、一对多还是多对一。

它不会自动替代业务代码里的新增、删除、权限校验和数据一致性判断。

joinedload ​

joinedload 是预加载方式之一。

使用它时,ORM 会在查询主表时,通过 join 把关联表也一起查出来。

例如查项目时,同时把负责人用户查出来。

这样后续访问 project.leader 时,不需要再额外发起查询。

生成的 SQL ​

使用 joinedload 后,SQL 里会出现类似左连接的结构。

主表字段会先出现在查询结果里。

关联表字段会追加在后面。

ORM 再根据关系定义,把关联表字段组装成对应的模型对象,塞进关系属性。

这就是它方便的地方。

也是它黑盒的来源。

一对多关系 ​

一个用户可以负责多个项目。

所以用户模型里可以定义:

py
projects

它是一个数组。

查询用户并预加载项目时,一个用户可能对应多条项目记录。

SQL 结果里用户行会膨胀。

ORM 需要把这些重复用户合并成一个用户对象,再把项目放进 projects 数组里。

为什么要 unique ​

一对多预加载会产生行膨胀。

例如李四负责四个项目。

SQL 查询结果里,李四可能出现四行。

但 Python 结果里,我们希望只有一个李四对象。

这时就要调用 unique 去重。

去重依据是主表对象的主键。

项目数据会被合并进主对象的数组属性。

未预加载访问关系属性 ​

如果没有使用 joinedload 或 selectinload 预加载,直接访问关系属性可能会报错。

尤其在异步 ORM 场景里,访问属性时 ORM 可能想发起延迟查询。

但当前上下文不允许它这么做,就会出现类似 MissingGreenlet 的错误。

所以关系属性不是普通字段。

访问前要确认是否已经预加载。

用计算属性抹平差异 ​

可以用计算属性包装关系属性。

如果关系已预加载,就返回真实数据。

如果未预加载并触发异常,就返回空值。

这样业务层访问时更稳定。

例如项目列表不需要负责人详情时,就不用强制预加载。

需要负责人详情时,再显式加预加载。

预加载不是默认必选 ​

不是每次查询用户都需要查项目。

不是每次查询项目都需要查负责人。

预加载要按接口需要选择。

否则简单列表接口也会带出一堆关联数据,性能会变差。

审批单与项目关系 ​

审批单表里会保存项目 ID。

一个项目可以有多张审批单。

一张审批单属于一个项目。

所以审批单和项目之间可以定义多对一关系。

项目模型里也可以定义审批单列表。

这样后续查项目时,可以把审批单一起带出来,再统计审批通过的金额。

多层关系 ​

关系可以继续嵌套。

例如:

  • 审批单关联项目;
  • 项目关联负责人;
  • 项目关联审批单列表。

多层预加载时,写法会比单层更复杂。

不同写法可能表示同级预加载,也可能表示沿着某个关系继续向下预加载。

这部分要结合生成的 SQL 看,不能只看 Python 代码猜结果。

SQL 日志很重要 ​

调试 ORM 关系查询时,要打开 SQL 日志。

这样才能看到它到底生成了什么 SQL。

如果只看关系属性,很多行为都是黑盒。

看 SQL 才能判断:

  • 是否做了 join;
  • 是否查了全表;
  • 是否字段过多;
  • 是否可能影响索引;
  • 是否发生行膨胀。

这一节的重点 ​

这一节把 ORM 关系查询讲到可调试层面。

你需要记住:

  • ORM 关系定义不是数据库外键;
  • joinedload 会通过 join 预加载关联数据;
  • 一对多预加载会导致行膨胀;
  • unique 用来合并重复主对象;
  • 未预加载时访问关系属性可能报错;
  • 复杂关系查询一定要看 SQL 日志。

AI Agent 课程学习文档。