最近在 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/
能有个大佬回复下吗 上次发了一个帖都没人回我 有点难受-。-
1
liulibzz OP |
2
lxy42 2019-07-02 00:13:41 +08:00 via Android
首先要明白懒加载的目的是为了尽量延迟执行 SQL 的时间,提高性能。
你举的前两个例子是会懒加载的,第三个由于用到 step 所以会执行 SQL,我觉得是因为 step 并不能对应到一条 SQL 语句,所以不能懒加载。 |
3
helsonxiao 2019-07-02 00:21:06 +08:00 via Android
查一下怎么记录数据库相关的 log,执行看看就知道了
|
4
anonymous256 2019-07-02 01:56:24 +08:00 via Android
你看下.all()这个方法的源码实现。 应该是用了 yield 关键字的,就是生成器。(之前有看过一眼,但不完全确定我说的对。) 生成器最大的特点是不会把返回全部对象都读入内存,只有在调用的时候,才会用 next 方法,逐个获取对象。
|
5
anonymous256 2019-07-02 02:13:52 +08:00 via Android
@anonymous256 #6 查了下,我的言论是错误的。
它用的是迭代器,不是生成器。QuerySet 是可迭代的,并且在第一次迭代时执行数据库查询。 当第一次进去循环时,检索数据并加载数据库,为每个行创建对象。然后得到可迭代的数据内容。 |
6
anonymous256 2019-07-02 02:33:17 +08:00 via Android
帮你翻译一下英文:
查询集通常返回另一个未求值的查询集。但是如果你使用了切片语法中的 step(步长?)参数(也就是你的第三个示例),那么 Django 会立刻执行数据库查询,并且返回一个 list。同对一个已求值的查询集取切片,也会返回一个列表。 |
7
cnanyi 2019-07-02 09:13:49 +08:00 1
django 的 settings 里把 sql 日志打开, 然后执行一下那些语句,看看生成的 sql,你就明白了
|
9
liulibzz OP @helsonxiao 好的 谢谢~
|
11
liulibzz OP @anonymous256 嗯嗯 谢谢 我去看看源码
|
12
vkhsyj 2019-07-02 09:44:25 +08:00
你想一想如果你实现 orm,怎么编译 sql
|
13
fuxiuyin 2019-07-02 10:40:26 +08:00
原文我没看,但是截图里面的两种是一样的,all()返回的是一个 QuerySet 对象,切片返回的也是一个 QuerySet 对象,只有迭代的时候才会去查询数据库。
|
14
fuxiuyin 2019-07-02 10:42:46 +08:00
我觉得他想说的是一个使用 limit 和 offset 让数据库做分页,也个是把所有数据都返回到 python 程序用列表的切片方法分页,但实际上真想用 python 的列表切分得 list(BlogPost.objects.all())[0:20]
|
15
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 是没有间隔取这个语法的。
|