Django过滤器与单个对象的获取?

时间:2009-06-19 16:33:12

标签: django django-models django-queryset

我和一些同事正在讨论这个问题。当你期望只有一个对象时,是否有一种首选的方法来检索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

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

有关哪一项更可取的任何想法?哪个更有效率?

13 个答案:

答案 0 :(得分:158)

get()专门针对此案例提供。使用它。

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

答案 1 :(得分:29)

您可以安装名为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

答案 2 :(得分:15)

1是正确的。在Python中,异常具有与返回相同的开销。有关简化证明,您可以查看this

2这就是Django在后端所做的事情。 get调用filter并在未找到任何项目或找到多个对象时引发异常。

答案 3 :(得分:9)

我参加派对有点晚了,但是对于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

答案 4 :(得分:8)

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

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

最后,第一个选项更短,省略了额外的临时变量 - 只是一个微小的差异,但每一点都有帮助。

答案 5 :(得分:8)

为什么这一切都有效?用1个内置快捷方式替换4行。 (这有自己的尝试/除外。)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

答案 6 :(得分:7)

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

但这是Django的ORM,可能是数据库的往返,甚至是缓存的结果,可能会主导性能特征,所以在这种情况下支持可读性,因为你只想到一个结果,使用{ {1}}。

答案 7 :(得分: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]

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

答案 8 :(得分:1)

有趣的问题,但对于我来说,选项#2过早优化。我不确定哪个性能更高,但选项#1肯定看起来和我感觉更加pythonic。

答案 9 :(得分:1)

我建议采用不同的设计。

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

结果非常棒,例如:

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

此处,filter返回空查询集或带有单个项的查询集。您的自定义查询集功能也是可链接和可重用的。如果您想为所有参赛作品执行此操作:MyModel.objects.all().yourFunction()

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

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

答案 10 :(得分:0)

选项1更优雅,但请务必使用try..except。

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

答案 11 :(得分:0)

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

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

Record.objects.get(pk=id)

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

我需要跑步:

Record.objects.filter(pk=id)

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

答案 12 :(得分:0)

.get()

  

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

     

get()引发MultipleObjectsReturn如果有多个对象   找到了。 MultipleObjectsReturned异常是   模型类。

     

get()会在未找到对象的情况下引发DidNotExist异常   给定的参数。该异常也是模型的一个属性   课。

.filter()

  

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

注意

  

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