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')
和 -
来反转元素。
【讨论】:
我试过这个并得到“不支持负索引。” @DarkCygnusProduct.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 - 限制查询结果的主要内容,如果未能解决你的问题,请参考以下文章
精确查找的QuerySet值必须使用Django中的切片错误限制为一个结果