【问题标题】:Django - limiting query resultsDjango - 限制查询结果
【发布时间】:2011-09-28 06:37:03
【问题描述】:

我想获取模型的最后 10 个实例并获得以下代码:

 Model.objects.all().order_by('-id')[:10]

是不是先把所有的实例都捡起来,然后再拿最后的10个? 有没有更有效的方法?

【问题讨论】:

标签: django


【解决方案1】:

Django 查询集是惰性的。这意味着只有当您特别要求结果时,查询才会访问数据库。

因此,在您打印或实际使用查询结果之前,您可以在不访问数据库的情况下进一步过滤。

正如您在下面看到的,您的代码仅执行一个 sql 查询以仅获取最后 10 个项目。

In [19]: import logging                                 
In [20]: l = logging.getLogger('django.db.backends')    
In [21]: l.setLevel(logging.DEBUG)                      
In [22]: l.addHandler(logging.StreamHandler())      
In [23]: User.objects.all().order_by('-id')[:10]          
(0.000) SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "auth_user" ORDER BY "auth_user"."id" DESC LIMIT 10; args=()
Out[23]: [<User: hamdi>]

【讨论】:

  • 我在 mongoDB 上试过这个,它说不支持 SELECT。如何在 mongoDB 上做到这一点?
  • @winux 因为这是 Django 特定的,听起来您可能需要考虑设置 Django 以专门用于 Mongo/NoSQL 类型的数据库。根据我的经验,这不是标准 Django ORM 设置的典型设置。
【解决方案2】:

实际上我认为LIMIT 10 将被发布到数据库,因此切片不会在 Python 中发生,而是在数据库中发生。

更多信息请参见limiting-querysets

【讨论】:

  • 请注意,这不适用于同样需要过滤的查询集,因为切片后无法过滤。
  • 所以先过滤再切片。感谢 Davor 提供链接!
【解决方案3】:

看起来问题中的解决方案不再适用于 Django 1.7 并引发错误: “一旦获取切片,就无法重新排序查询”

根据文档https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets,强制 Python 切片语法的“step”参数评估查询。它是这样工作的:

Model.objects.all().order_by('-id')[:10:1]

我仍然想知道限制是在 SQL 中执行还是在 Python 中对返回的整个结果数组进行切片。将巨大的列表检索到应用程序内存是没有好处的。

【讨论】:

  • 即使这个解决方案也不适用于 django>=1.8 测试。
【解决方案4】:

是的。如果您想获取有限的对象子集,可以使用以下代码:

例子:

obj=emp.objects.all()[0:10]

开头的0是可选的,所以

obj=emp.objects.all()[:10]

以上代码返回前 10 个实例。

【讨论】:

    【解决方案5】:

    QuerySets 的切片返回 list,这意味着如果你喜欢:

    >>> Model.objects.all().order_by('-id')[:10]
    

    它将返回一个列表,问题是您无法在 list 上执行更多 QuerySet 方法

    所以如果你想对返回的结果做更多​​的事情,你可以:

    >>> limit = 5 # your choice
    >>>
    >>> m1 = Model.objects.filter(pk__gte=Model.objects.count() - limit) # last five
    >>> m2 = Model.objects.filter(pk__lte=limit)  # first five
    

    现在您可以执行更多方法:

    # Just for illustration
    >>> m2.annotate(Avg("some_integer_column")) # annotate
    >>> m2.annotate(Sum("some_integer_column"))
    >>> m2.aggregate(Sum("some_integer_column")) # aggregate
    

    通过使用 slice 表示法([]) 来限制结果,您还可以将能力限制为chain QuerySet methods

    如果您非常确定不需要进行任何进一步的查询,那么切片就可以解决问题。

    【讨论】:

      【解决方案6】:

      作为对其他有用答案的补充和观察,值得注意的是,实际上将[:10] 作为切片将返回列表的前 10 个元素,而不是最后 10 个...

      要获得最后 10 个,您应该改为使用[-10:](请参阅here)。这将帮助您避免使用 order_by('-id')- 来反转元素。

      【讨论】:

      • 我试过这个并得到“不支持负索引。”
      • @DarkCygnus Product.objects.filter(~Q(price=0))[-5:] 导致我出现同样的错误:“不支持负索引。”
      • 这在 django 中对查询集不起作用:code.djangoproject.com/ticket/13089 如果您将查询集转换为列表,它将起作用。
      • @valem 将查询集转换为列表将覆盖 django 的惰性计算并导致从数据库中获取大量数据集
      • @Navaneethan 是的,这是真的。但与此答案无关
      【解决方案7】:

      过滤器问题的简单答案

      Notification.objects.filter(user=request.user).order_by("-id")[:limit]
      

      只需输入order_by,然后输入[:limit]

      【讨论】:

        猜你喜欢
        • 2021-03-26
        • 1970-01-01
        • 2017-10-13
        • 1970-01-01
        • 1970-01-01
        • 2014-08-04
        • 2016-04-01
        • 2011-01-17
        • 2020-10-07
        相关资源
        最近更新 更多