Django filter versus get for single object?

2019-01-16 02:30发布

I was having a debate on this with some colleagues. Is there a preferred way to retrieve an object in Django when you're expecting only one?

The two obvious ways are:

   try: 
      obj = MyModel.objects.get(id=1)
   except MyModel.DoesNotExist:
      # we have no object!  do something
      pass

and

   objs = MyModel.objects.filter(id=1)
   if len(objs) == 1:
      obj = objs[0]
   else: 
      # we have no object!  do something
      pass

The first method seems behaviorally more correct, but uses exceptions in control flow which may introduce some overhead. The second is more roundabout but won't ever raise an exception.

Any thoughts on which of these is preferable? Which is more efficient?

11条回答
Deceive 欺骗
2楼-- · 2019-01-16 02:53

Option 1 is more elegant, but be sure to use try..except.

From my own experience I can tell you that sometimes you're sure there cannot possibly be more than one matching object in the database, and yet there will be two... (except of course when getting the object by its primary key).

查看更多
别忘想泡老子
3楼-- · 2019-01-16 02:54

I can't speak with any experience of Django but option #1 clearly tells the system that you are asking for 1 object, whereas the second option does not. This means that option #1 could more easily take advantage of cache or database indexes, especially where the attribute you're filtering on is not guaranteed to be unique.

Also (again, speculating) the second option may have to create some sort of results collection or iterator object since the filter() call could normally return many rows. You'd bypass this with get().

Finally, the first option is both shorter and omits the extra temporary variable - only a minor difference but every little helps.

查看更多
We Are One
4楼-- · 2019-01-16 03:00

You can install a module called django-annoying and then do this:

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
查看更多
我命由我不由天
5楼-- · 2019-01-16 03:03

I suggest a different design.

If you want to perform a function on a possible result, you could derive from QuerySet, like this: http://djangosnippets.org/snippets/734/

The result is pretty awesome, you could for example:

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

Here, filter returns either an empty queryset or a queryset with a single item. Your custom queryset functions are also chainable and reusable. If you want to perform it for all your entries: MyModel.objects.all().yourFunction().

They are also ideal to be used as actions in the admin interface:

def yourAction(self, request, queryset):
    queryset.yourFunction()
查看更多
看我几分像从前
6楼-- · 2019-01-16 03:05

Why do all that work? Replace 4 lines with 1 builtin shortcut. (This does its own try/except.)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)
查看更多
登录 后发表回答