prefetch_related
考虑如下两个模型:
现在遍历所有书籍并访问每本书的作者:
假设数据库中有 N 本书,上述代码会先执行一次数据库查询获取所有书籍,然后针对每本书籍查询一次作者,总共查询 N+1 次,即 N+1 查询问题。
此问题可能出现在 多对多 或者 反向一对多(反向外键)的查询情境中。
Author 表被反复查询了 N 次(每次都需要遍历整张表),那么为什么不将这 N 次查询合并为一次查询呢?这就需要用到 prefetch_related
。
嵌套的预加载
考虑如下三个模型:
如果需要访问 Book 查询集中 Book 对象的 .authors 集合,以及 .authors 集合中的 Author 对象关联的 Publisher 对象,可以使用嵌套预加载,只需要三次查询就可以取出所有数据。
嵌套对象间使用 __
隔开即可,多对多关联的用复数,一对一或者外键关联的用单数。
自定义预加载查询集
上述的 Book.objects.prefetch_related("authors").all()
会预加载整个 Author 表,也可以使用 models.Prefetch
预加载部分 Author 数据。
如果自定义查询集 sub_authors 确实已经包含了所有需要访问到的作者,将不再执行查询。
如果某个 book.authors 中存在一个 Author 对象,它不在预加载的查询集 sub_authors 中,当我们实际访问到这个 author 时,还是会触发数据库查询。
所以使用自定义预加载查询集时,请务必确保所有可能用到的关联对象都在预加载查询集中。
Prefetch
的构造函数如下:
使用 to_attr
可以将预加载的结果附加到指定的属性上(直接新增属性!)而不是已有的属性上。
select_related
上面的一对一或者外键关联还是存在 N+1 查询问题。
针对 一对一 或者 外键 关联,我们使用 select_related
处理 N+1 查询问题。
同样支持嵌套:
select_related 会减少数据库查询次数,但是会增大第一次查询的工作量,尤其是数据量很大的情况下。
评论区