160

我正在和一些同事就这个问题进行辩论。当您只需要一个对象时,是否有一种首选方法可以在 Django 中检索对象?

两种明显的方式是:

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

和:

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

第一种方法在行为上似乎更正确,但在控制流中使用了异常,这可能会引入一些开销。第二个更迂回,但永远不会引发异常。

关于这些中哪一个更可取的任何想法?哪个更有效率?

4

14 回答 14

193

get()专门针对这种情况提供的。用它。

选项 2 几乎正是该get()方法在 Django 中的实际实现方式,因此应该没有“性能”差异(而且您正在考虑它的事实表明您违反了编程的基本规则之一,即试图在编写和分析代码之前优化代码——在你拥有代码并可以运行它之前,你不知道它将如何执行,并且在此之前尝试优化是一条痛苦的道路)。

于 2009-06-19T20:04:19.443 回答
31

您可以安装一个名为django-annoying的模块,然后执行以下操作:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff
于 2009-06-20T02:19:06.907 回答
18

1 是正确的。在 Python 中,异常的开销与返回的开销相同。对于一个简化的证明,你可以看看这个

2 这就是 Django 在后端所做的。如果没有找到项目或找到多个对象,则get调用并引发异常。filter

于 2009-06-19T20:08:30.043 回答
15

我参加聚会有点晚了,但是在 Django 1.6 中有first()关于查询集的方法。

https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。如果 QuerySet 没有定义排序,则查询集会自动按主键排序。

例子:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None
于 2014-05-14T10:11:18.900 回答
9

为什么所有这些工作?用 1 个内置快捷方式替换 4 行。(这有自己的尝试/除外。)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)
于 2009-06-20T01:25:03.430 回答
8

我无法谈论 Django 的任何经验,但选项 #1 清楚地告诉系统您正在请求 1 个对象,而第二个选项则没有。这意味着选项 #1 可以更轻松地利用缓存或数据库索引,尤其是在您过滤的属性不能保证唯一的情况下。

此外(再次推测)第二个选项可能必须创建某种结果集合或迭代器对象,因为 filter() 调用通常会返回许多行。你可以用 get() 绕过它。

最后,第一个选项既短又省略了额外的临时变量——只有很小的区别,但每一点都有帮助。

于 2009-06-19T16:45:23.160 回答
7

有关异常的更多信息。如果他们不被提高,他们几乎没有成本。因此,如果您知道您可能会得到结果,请使用异常,因为使用条件表达式您每次都要付出检查的成本,无论如何。另一方面,当它们被提出时,它们的成本比条件表达式要多一点,所以如果你不希望得到某个频率的结果(比如,30% 的时间,如果内存服务的话),条件检查结果是要便宜一点。

但这是 Django 的 ORM,可能是到数据库的往返,甚至是缓存的结果,很可能会支配性能特征,所以有利于可读性,在这种情况下,因为您期望只有一个结果,所以使用get().

于 2009-06-20T01:32:03.853 回答
4

我玩过这个问题,发现选项 2 执行两个 SQL 查询,对于这样一个简单的任务来说,这是多余的。看我的注释:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

执行单个查询的等效版本是:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

通过切换到这种方法,我能够大大减少我的应用程序执行的查询数量。

于 2013-01-16T11:46:08.337 回答
2

。得到()

返回与给定查找参数匹配的对象,该参数应采用字段查找中描述的格式。

如果找到多个对象,get() 将引发 MultipleObjectsReturned。MultipleObjectsReturned 异常是模型类的一个属性。

如果未找到给定参数的对象,get() 将引发 DoesNotExist 异常。这个异常也是模型类的一个属性。

。筛选()

返回一个新的 QuerySet,其中包含与给定查找参数匹配的对象。

笔记

当您想要获取单个唯一对象时使用 get(),当您想要获取与查找参数匹配的所有对象时使用 filter()。

于 2019-12-25T18:54:14.540 回答
1

有趣的问题,但对我来说,选项 #2 带有过早优化的味道。我不确定哪个性能更高,但选项#1 对我来说确实看起来和感觉更 Pythonic。

于 2009-06-19T16:36:26.263 回答
1

我建议一个不同的设计。

如果要对可能的结果执行功能,可以从 QuerySet 派生,如下所示:http ://djangosnippets.org/snippets/734/

结果非常棒,例如,您可以:

MyModel.objects.filter(id=1).yourFunction()

在这里,过滤器返回一个空查询集或一个包含单个项目的查询集。您的自定义查询集函数也是可链接和可重用的。如果您想为所有条目执行它:MyModel.objects.all().yourFunction().

它们也非常适合用作管理界面中的操作:

def yourAction(self, request, queryset):
    queryset.yourFunction()
于 2012-08-15T07:50:27.500 回答
0

选项 1 更优雅,但一定要使用 try..except。

根据我自己的经验,我可以告诉您,有时您确定数据库中不可能有多个匹配的对象,但仍然会有两个……(当然,通过主键获取对象时除外)。

于 2009-06-22T11:40:02.477 回答
0

很抱歉在这个问题上再添加一个,但我正在使用 django 分页器,在我的数据管理应用程序中,允许用户选择要查询的内容。有时这是文档的 id,但除此之外它是返回多个对象的一般查询,即 Queryset。

如果用户查询 id,我可以运行:

Record.objects.get(pk=id)

这会在 django 的分页器中引发错误,因为它是一个记录而不是记录的查询集。

我需要运行:

Record.objects.filter(pk=id)

它返回一个包含一个项目的查询集。然后分页器工作得很好。

于 2019-10-22T10:16:46.893 回答
0

".get()"可以返回一个带有花括号 "{}" 的最外对象

{
  "name": "John",
  "age": "26",
  "gender": "Male"
}

".filter()"可以返回一个或多个对象的列表(集合),其中最外的方括号 "[]"

[
  {
    "name": "John",
    "age": "26",
    "gender": "Male"
  },
  {
    "name": "Tom",
    "age": "18",
    "gender": "Male"
  },
  {
    "name": "Marry",
    "age": "22",
    "gender": "Female"
  }
]
于 2022-02-25T20:15:04.147 回答