django queryset 懒加载问题请教

2019-07-01 23:43:09 +08:00
 liulibzz

最近在 CSDN 上看见了一个博客是这样写的,博客地址 https://blog.csdn.net/zgyulongfei/article/details/8842338

他这里说这两种是不同的,一个是懒加载一个则会马上访问数据库

然后我去看了下官方文档,是这样写的,英语太差,没太看明白,想请教下,这里的意思是我理解的这样的吗 第一种和第二种是一样的都是懒加载 第三种则会直接访问数据库

第一种

posts = BlogPost.objects.all()[0:20]

第二种

posts = BlogPost.objects.all()
posts = posts[0:20]

第三种

posts = BlogPost.objects.all()[0:20:5]

官方文档原文

QuerySet usually returns another unevaluated QuerySet, but Django will execute the database query if you use the “ step ” parameter of slice syntax, and will return a list. Slicing a QuerySet that has been evaluated also returns a list.

地址 https://docs.djangoproject.com/en/2.2/ref/models/querysets/

能有个大佬回复下吗 上次发了一个帖都没人回我 有点难受-。-

2917 次点击
所在节点    Python
15 条回复
liulibzz
2019-07-01 23:43:42 +08:00
lxy42
2019-07-02 00:13:41 +08:00
首先要明白懒加载的目的是为了尽量延迟执行 SQL 的时间,提高性能。

你举的前两个例子是会懒加载的,第三个由于用到 step 所以会执行 SQL,我觉得是因为 step 并不能对应到一条 SQL 语句,所以不能懒加载。
helsonxiao
2019-07-02 00:21:06 +08:00
查一下怎么记录数据库相关的 log,执行看看就知道了
anonymous256
2019-07-02 01:56:24 +08:00
你看下.all()这个方法的源码实现。 应该是用了 yield 关键字的,就是生成器。(之前有看过一眼,但不完全确定我说的对。) 生成器最大的特点是不会把返回全部对象都读入内存,只有在调用的时候,才会用 next 方法,逐个获取对象。
anonymous256
2019-07-02 02:13:52 +08:00
@anonymous256 #6 查了下,我的言论是错误的。

它用的是迭代器,不是生成器。QuerySet 是可迭代的,并且在第一次迭代时执行数据库查询。 当第一次进去循环时,检索数据并加载数据库,为每个行创建对象。然后得到可迭代的数据内容。
anonymous256
2019-07-02 02:33:17 +08:00
帮你翻译一下英文:

查询集通常返回另一个未求值的查询集。但是如果你使用了切片语法中的 step(步长?)参数(也就是你的第三个示例),那么 Django 会立刻执行数据库查询,并且返回一个 list。同对一个已求值的查询集取切片,也会返回一个列表。
cnanyi
2019-07-02 09:13:49 +08:00
django 的 settings 里把 sql 日志打开, 然后执行一下那些语句,看看生成的 sql,你就明白了
liulibzz
2019-07-02 09:41:21 +08:00
@lxy42 好的谢谢~
liulibzz
2019-07-02 09:41:45 +08:00
@helsonxiao 好的 谢谢~
liulibzz
2019-07-02 09:42:08 +08:00
@cnanyi 好的 谢谢~
liulibzz
2019-07-02 09:43:00 +08:00
@anonymous256 嗯嗯 谢谢 我去看看源码
vkhsyj
2019-07-02 09:44:25 +08:00
你想一想如果你实现 orm,怎么编译 sql
fuxiuyin
2019-07-02 10:40:26 +08:00
原文我没看,但是截图里面的两种是一样的,all()返回的是一个 QuerySet 对象,切片返回的也是一个 QuerySet 对象,只有迭代的时候才会去查询数据库。
fuxiuyin
2019-07-02 10:42:46 +08:00
我觉得他想说的是一个使用 limit 和 offset 让数据库做分页,也个是把所有数据都返回到 python 程序用列表的切片方法分页,但实际上真想用 python 的列表切分得 list(BlogPost.objects.all())[0:20]
fuxiuyin
2019-07-02 10:51:13 +08:00
以及第三种,指定 step 会直接查询数据并且返回一个 list 对象,这么干主要是为了防止在用带有 step 的切分以后再附加 filter 之类的条件,因为这样会产生歧义。比如如果可以,BlogPost.objects.all()[0:20:5].filter(name="aaa"),调用者希望的应该是返回“头 20 个对象当中每隔 5 个取一个且满足 name 为‘ aaa ’”即返回的对象会小于等于 5 个,但实际上懒加载的话这句话真正的作用是,BlogPost.objects.all().filter(name="aaa")[0:20:5],即先筛选后间隔取。因为 ORM 实际上就是拼接 SQL 然后让数据库做计算,如果要支持先间隔取再筛选只能自己实现筛选逻辑,因为大部分数据库支持的 SQL 是没有间隔取这个语法的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/579131

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX