我和一些同事正在讨论这个问题。当你期望只有一个对象时,是否有一种首选的方法来检索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
第一种方法似乎在行为上更正确,但在控制流中使用异常可能会引入一些开销。第二个更环形,但不会引发异常。
有关哪一项更可取的任何想法?哪个更有效率?
答案 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() 当您要获取所有与查找参数匹配的对象时。