Djangofilter与获取单个对象?

我正在和一些同事讨论这个问题。 有一个首选的方法来检索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 

第一种方法似乎在行为上更正确,但在控制stream中使用exception,这可能会引入一些开销。 第二个是更多的迂回,但永远不会例外。

任何想法哪个更好? 哪个更有效率?

get()是专门为这种情况提供的。 用它。

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

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

1是正确的。 在Python中,exception与返回相同。 对于一个简单的certificate,你可以看看这个 。

2这是Django在后端做的事情。 get调用filter ,如果找不到任何项目或find多于一个对象,则引发exception。

我不能说任何Django的经验,但选项#1清楚地告诉系统,您要求1个对象,而第二个选项不是。 这意味着选项#1可以更容易地利用caching或数据库索引,特别是在您要过滤的属性不保证是唯一的情况下。

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

最后,第一个选项既短又省略额外的临时variables – 只是一个小的差异,但每一个小的帮助。

一些关于exception的更多信息。 如果他们不提高,他们几乎没有成本。 因此,如果你知道你可能会得到一个结果,使用这个exception,因为使用一个条件expression式,你无论如何都要支付每一次检查的代价。 另一方面,当它们被提升时,它们的成本比条件expression式要多一些,所以如果你期望没有某个频率的结果(例如30%的时间,如果内存服务),条件检查结果要便宜一点。

但是这是Django的ORM,可能是数据库的往返行为,或者甚至caching的结果,可能会主宰性能特性,所以在这种情况下,由于您期望get()一个结果,请使用get()

为什么所有这些工作? 用内置的快捷键replace4行。 (这是自己的尝试/除了。)

 from django.shortcuts import get_object_or_404 obj = get_object_or_404(MyModel, id=1) 

我晚了一点,但是在Django 1.6中,querysets上有第first()方法。

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


返回查询集匹配的第一个对象,如果没有匹配对象,则返回None。 如果QuerySet没有定义的顺序,那么查询集由主键自动sorting。

例:

 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 

我已经玩了一下这个问题,并发现选项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] 

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

有趣的问题,但对我来说,选项#2的过早优化。 我不确定哪一个更高性能,但是选项#1当然会让我感觉更加pythonic。

我build议一个不同的devise。

如果你想对可能的结果执行一个函数,你可以从QuerySet派生,像这样: http : //djangosnippets.org/snippets/734/

结果是非常棒的,你可以例如:

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

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

它们也非常适合在pipe理界面中用作操作:

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

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

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