V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
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
liulibzz
V2EX  ›  Python

django queryset 懒加载问题请教

  •  
  •   liulibzz · Jul 1, 2019 · 3409 views
    This topic created in 2498 days ago, the information mentioned may be changed or developed.

    最近在 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 replies    2019-07-02 10:51:13 +08:00
    liulibzz
        1
    liulibzz  
    OP
       Jul 1, 2019
    lxy42
        2
    lxy42  
       Jul 2, 2019 via Android
    首先要明白懒加载的目的是为了尽量延迟执行 SQL 的时间,提高性能。

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

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

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