首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
V2EX  ›  Python

django queryset 懒加载问题请教

  •  
  •   liulibzz · 159 天前 · 1312 次点击
    这是一个创建于 159 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    avatar

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

    第一种

    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/

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

    15 回复  |  直到 2019-07-02 10:51:13 +08:00
        2
    lxy42   159 天前 via Android
    首先要明白懒加载的目的是为了尽量延迟执行 SQL 的时间,提高性能。

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

    它用的是迭代器,不是生成器。QuerySet 是可迭代的,并且在第一次迭代时执行数据库查询。 当第一次进去循环时,检索数据并加载数据库,为每个行创建对象。然后得到可迭代的数据内容。
        6
    anonymous256   159 天前 via Android
    帮你翻译一下英文:

    查询集通常返回另一个未求值的查询集。但是如果你使用了切片语法中的 step(步长?)参数(也就是你的第三个示例),那么 Django 会立刻执行数据库查询,并且返回一个 list。同对一个已求值的查询集取切片,也会返回一个列表。
        7
    cnanyi   158 天前   ♥ 1
    django 的 settings 里把 sql 日志打开, 然后执行一下那些语句,看看生成的 sql,你就明白了
        8
    liulibzz   158 天前
    @lxy42 好的谢谢~
        9
    liulibzz   158 天前
    @helsonxiao 好的 谢谢~
        10
    liulibzz   158 天前
    @cnanyi 好的 谢谢~
        11
    liulibzz   158 天前
    @anonymous256 嗯嗯 谢谢 我去看看源码
        12
    vkhsyj   158 天前
    你想一想如果你实现 orm,怎么编译 sql
        13
    fuxiuyin   158 天前
    原文我没看,但是截图里面的两种是一样的,all()返回的是一个 QuerySet 对象,切片返回的也是一个 QuerySet 对象,只有迭代的时候才会去查询数据库。
        14
    fuxiuyin   158 天前
    我觉得他想说的是一个使用 limit 和 offset 让数据库做分页,也个是把所有数据都返回到 python 程序用列表的切片方法分页,但实际上真想用 python 的列表切分得 list(BlogPost.objects.all())[0:20]
        15
    fuxiuyin   158 天前
    以及第三种,指定 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 是没有间隔取这个语法的。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   955 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 19:27 · PVG 03:27 · LAX 11:27 · JFK 14:27
    ♥ Do have faith in what you're doing.