247
votes

Often I find myself wanting to get the first object from a queryset in Django, or return None if there aren't any. There are lots of ways to do this which all work. But I'm wondering which is the most performant.

qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
    return qs[0]
else:
    return None

Does this result in two database calls? That seems wasteful. Is this any faster?

qs = MyModel.objects.filter(blah = blah)
if len(qs) > 0:
    return qs[0]
else:
    return None

Another option would be:

qs = MyModel.objects.filter(blah = blah)
try:
    return qs[0]
except IndexError:
    return None

This generates a single database call, which is good. But requires creating an exception object a lot of the time, which is a very memory-intensive thing to do when all you really need is a trivial if-test.

How can I do this with just a single database call and without churning memory with exception objects?

8
Rule of thumb: If you're worried about minimizing DB round-trips, don't use len() on querysets, always use .count().Daniel DiPaolo
"creating an exception object a lot of the time, which is a very memory-intensive thing" - if you're concerned about creating one extra exception, then you're doing it wrong as Python uses exceptions all over the place. Did you actually benchmarked that it's memory-intensive in your case?lqc
@Leopd And if you'd actually benchmarked the anwser in any way (or at least the comments), you would know it's not any faster. It actually may be slower, 'cause your creating an extra list just to throw it out. And all that is just peanuts compared to the cost of calling a python function or using Django's ORM in the first place! A single call to filter() is many, many, many times slower then raising an exception (which is still gonna be raised, 'cause that's how iterator protocol works!).lqc
Your intuition is correct that the performance difference is small, but your conclusion is wrong. I did run a benchmark and the accepted answer is in fact faster by a real margin. Go figure.Leopd
For folks using Django 1.6, they've finally added the first() and last() convenience methods: docs.djangoproject.com/en/dev/ref/models/querysets/#firstWei Yen

8 Answers

409
votes

Use the convenience methods .first() and .last():

MyModel.objects.filter(blah=blah).first()

They both swallow the resulting exception and return None if the queryset returns no objects.

These were added in Django 1.6, which was released in Nov 2013.

153
votes

You can use array slicing:

Entry.objects.all()[:1].get()

Which can be used with .filter():

Entry.objects.filter()[:1].get()

You wouldn't want to first turn it into a list because that would force a full database call of all the records. Just do the above and it will only pull the first. You could even use .order_by() to ensure you get the first you want.

Be sure to add the .get() or else you will get a QuerySet back and not an object.

48
votes
r = list(qs[:1])
if r:
  return r[0]
return None
41
votes

Now, in Django 1.9 you have first() method for querysets.

YourModel.objects.all().first()

This is a better way than .get() or [0] because it does not throw an exception if queryset is empty, Therafore, you don't need to check using exists()

7
votes

This could work as well:

def get_first_element(MyModel):
    my_query = MyModel.objects.all()
    return my_query[:1]

if it's empty, then returns an empty list, otherwise it returns the first element inside a list.

6
votes

If you plan to get first element often - you can extend QuerySet in this direction:

class FirstQuerySet(models.query.QuerySet):
    def first(self):
        return self[0]


class ManagerWithFirstQuery(models.Manager):
    def get_query_set(self):
        return FirstQuerySet(self.model)

Define model like this:

class MyModel(models.Model):
    objects = ManagerWithFirstQuery()

And use it like this:

 first_object = MyModel.objects.filter(x=100).first()
4
votes

It can be like this

obj = model.objects.filter(id=emp_id)[0]

or

obj = model.objects.latest('id')
3
votes

You should use django methods, like exists. Its there for you to use it.

if qs.exists():
    return qs[0]
return None