Django - 限制查询结果

Posted

技术标签:

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

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

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

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

【问题讨论】:

docs.djangoproject.com/en/dev/topics/db/queries/… 【参考方案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]

【讨论】:

以上是关于Django - 限制查询结果的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django Rest 过滤器限制查询结果

精确查找的QuerySet值必须使用Django中的切片错误限制为一个结果

Django prefetch_related 与限制

Django:限制 models.ForeignKey 结果

Django 石墨烯中继限制对用户拥有的对象的查询

Django数据操作