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 去重。

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

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

未预加载访问关系属性

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

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

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

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

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

用计算属性抹平差异

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

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

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

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

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

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

预加载不是默认必选

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

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

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

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

审批单与项目关系

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

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

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

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

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

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

多层关系

关系可以继续嵌套。

例如:

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

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

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

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

SQL 日志很重要

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

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

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

看 SQL 才能判断:

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

这一节的重点

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

你需要记住:

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

AI Agent 课程学习文档。