. filter() vs. get() for single object? (Django)

我正在和同事讨论这个问题。在 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

第一种方法在行为上似乎更正确,但是在控制流中使用异常可能会引起一些开销。第二种方式更为迂回,但永远不会提出例外。

你觉得哪个更好? 哪个更有效率?

126394 次浏览

有趣的问题,但是对我来说第二个选项是过早的优化。我不确定哪一个更有表现力,但是选项1对我来说看起来和感觉起来都更像蟒蛇。

我不能说任何经验的 Django,但选项 # 1清楚地告诉系统,您要求1个对象,而第二个选项不。这意味着选项 # 1可以更容易地利用缓存或数据库索引,特别是当您正在筛选的属性不一定是唯一的时候。

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

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

提供 get() 。使用它。

选项2几乎正是 get()方法在 Django 中实现的方式,所以不应该存在“性能”差异(事实上,你正在考虑它表明你正在违反编程的基本规则之一,即尝试优化代码之前,甚至已经编写和配置——直到你有代码并可以运行它,你不知道它将如何执行,尝试优化之前是一条痛苦的道路)。

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

这就是姜戈在后台做的事。如果没有找到项或者找到多个对象,则 get调用 filter并引发异常。

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

from django.shortcuts import get_object_or_404


obj = get_object_or_404(MyModel, id=1)

更多关于异常的信息。如果没有养殖,它们几乎不需要花费任何成本。因此,如果您知道您可能会得到一个结果,那么使用异常,因为使用条件表达式无论如何都要支付每次检查的成本。另一方面,当它们被提出时,它们的成本比条件表达式稍微高一点,所以如果你期望不会有某种频率的结果(比如,30% 的时间,如果内存没问题的话) ,条件检查结果会更便宜一些。

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

您可以安装一个名为 姜戈,烦人的模块,然后执行以下操作:

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

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

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

我建议换个设计。

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

结果非常棒,你可以举个例子:

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

在这里,filter 返回空查询集或带有单个项的查询集。您的自定义查询集函数也是可链接和可重用的。如果希望对所有条目执行此操作,则为: MyModel.objects.all().yourFunction()

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

def yourAction(self, request, queryset):
queryset.yourFunction()

我对这个问题进行了一些处理,发现选项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]

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

我有点晚了,但是对于 Django 1.6,在查询集上有 first()方法。

Https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query 查询首先


返回与查询集匹配的第一个对象,如果没有匹配的对象,返回“无”。如果 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

很抱歉在这个问题上再添加一个,但是我正在使用 django 分页器,并且在我的数据管理应用程序中,用户可以选择要查询的内容。有时,这是一个文档的 id,但在其他情况下,它是一个返回多个对象(即 Queryset)的常规查询。

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

Record.objects.get(pk=id)

它在 django 的分页器中抛出一个错误,因为它是 Record 而不是 Queryset of Records。

我得走了:

Record.objects.filter(pk=id)

它返回一个包含一个项目的查询集,然后分页器就可以正常工作了。

。得到()

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

Get ()引发 MultipleObjectsReturn (如果有多个对象) MultipleObjectsReturn 异常是 模特班。

如果找不到对象,get ()会引发 DoesNotExist 异常 这个异常也是模型的一个属性 同学们。

。过滤()

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

注意

当希望获取单个惟一对象时使用 get ()和 filter () 当您希望获取与查找参数匹配的所有对象时。

“ . 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"
}
]