我有一些同事在这一场辩论。 有检索在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
第一种方法似乎更行为上是正确的,但使用控制流异常,其可能引入一些开销。 二是更加迂回,但永远不会引发异常。
任何想法上的这些最好? 哪个更有效?
Answer 1:
get()
是专门为这种情况提供。 用它。
选项2几乎是如何精确的get()
方法在Django实际执行,所以不应该有“表演”的区别(以及你想它表明您违反程序设计的基本规则的事实,即尝试它甚至被写入及异形之前优化代码 - 直到你的代码,并可以运行它,你不知道这将如何执行,并试图在此之前,以优化是痛苦的路径)。
Answer 2:
你可以安装一个叫做模块Django的烦人 ,然后做到这一点:
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
Answer 3:
1是正确的。 在Python异常具有相同的开销来一回。 为了简化证明你可以看看这个 。
2这是Django的在后端做。 get
电话filter
,如果没有项目被发现抛出一个异常,或者一个以上的对象中找到。
Answer 4:
我有点迟到了,但与Django的1.6存在的first()
上查询集方法。
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
如果不存在匹配的对象,则返回由查询集,或无匹配的第一个对象。 如果查询集没有定义排序,然后将查询集自动由主键排序。
例:
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
Answer 5:
我不能使用Django的任何经验,但选项#1讲清楚告诉你所要求的1个对象的系统,而第二个选项没有。 这意味着,如果你在过滤的属性不保证是唯一的选项#1可以更轻松地高速缓存或数据库索引的优势,尤其是。
此外(再次,猜测),第二个选项可能会由于过滤器()调用创建某种结果集合或迭代器对象能够正常返回许多行。 你会绕过这个以get()。
最后,第一个选项是既短,省去了额外的临时变量 - 只有微小的差别,但每一个小小的帮助。
Answer 6:
为什么所有的工作? 替换4行1项内置的快捷方式。 (这确实它自己的try /除外。)
from django.shortcuts import get_object_or_404
obj = get_object_or_404(MyModel, id=1)
Answer 7:
关于异常的一些更多的信息。 如果他们不提高,他们的成本几乎为零。 因此,如果你知道你可能将有结果,使用异常,因为使用条件表达式你付出检查每一次,不管什么成本。 在另一方面,他们花费多一点比当他们提出了一个条件表达式,因此,如果您希望不要有一些频率的结果(比如工作时间的30%,如果没有记错),条件检查证明是有点便宜。
但是,这是Django的ORM,也可能是往返到数据库,甚至是缓存的结果,很可能主宰的性能特点,因此有利于可读性,在这种情况下,你希望只有一个结果,使用get()
。
Answer 8:
我这个问题打了一下,发现选项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]
通过切换到这种做法,我能够大幅降低查询我的应用程序执行的数量。
Answer 9:
有趣的问题,但对我的选择#2恶臭过早的优化。 我不知道这是更好的性能,但选择#1肯定的外观和感觉更Python给我。
Answer 10:
我提出一个不同的设计。
如果你想在一个可能的结果执行功能,您可以从查询集衍生,像这样: http://djangosnippets.org/snippets/734/
其结果是相当真棒,例如,您可以:
MyModel.objects.filter(id=1).yourFunction()
在这里,过滤器返回一个空查询集或单个项目一个查询集。 您的自定义查询集功能也可链接的,可重复使用。 如果要执行它的所有条目: MyModel.objects.all().yourFunction()
他们也是理想的用作在管理界面操作:
def yourAction(self, request, queryset):
queryset.yourFunction()
Answer 11:
选项1是更优雅,但一定要使用try..except。
从我自己的经验,我可以告诉你,有时候你肯定是不可能有一个以上的匹配对象在数据库中,仍然会有两个......(当然,除了通过主键获取对象时)。
文章来源: Django filter versus get for single object?