293

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?

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Leopd
  • 41,333
  • 31
  • 129
  • 167
  • 32
    Rule of thumb: If you're worried about minimizing DB round-trips, don't use `len()` on querysets, always use `.count()`. – Daniel DiPaolo Feb 25 '11 at 23:41
  • 8
    "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 Jul 03 '12 at 05:30
  • 1
    @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 Jul 05 '12 at 22:02
  • 1
    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 Jul 06 '12 at 06:11
  • 11
    For folks using Django 1.6, they've finally added the `first()` and `last()` convenience methods: https://docs.djangoproject.com/en/dev/ref/models/querysets/#first – Wei Yen Mar 12 '14 at 03:55
  • @DanielDiPaolo - this is poor rule of thumb. Devs need to understand when QuerySets are evaluated. If you are going to use the records anyway, using `len` is better. See https://docs.djangoproject.com/en/dev/topics/db/optimization/#don-t-overuse-count-and-exists – spookylukey Sep 18 '16 at 18:56

9 Answers9

479

Django 1.6 (released Nov 2013) introduced the convenience methods first() and last() which swallow the resulting exception and return None if the queryset returns no objects.

cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
  • 2
    it doesn't do the [:1], so it's not as fast (unless you need to evaluate the whole queryset anyway). – janek37 Feb 26 '16 at 21:16
  • 26
    also, `first()` and `last()` enforce an `ORDER BY` clause on a query. It will make the results deterministic but will most probably slow the query down. – Phil Krylov Jun 27 '17 at 19:24
  • @janek37 there are no differences in performance. As indicated by cod3monk3y, it is a convenient method and it doesn't read the entire queryset. – Zompa Sep 11 '19 at 09:55
  • 1
    @Zompa is incorrect. *THERE IS A DIFFERENCE IN PERFORMANCE* due to the enforced `ORDER BY` @Phil Krylov pointed out, which `[:1]` avoids. – theannouncer Mar 10 '21 at 22:27
  • 1
    Rolled back edit, which added no value except rewording and took the original suggestion out of context. I am not saying that first() and last() are the *fastest* way, for performance, merely that these methods exist, are useful, and convenient. There is no claim that this will answer the OP's goal of peformance. But clearly I and others have found this information marginally useful. – cod3monk3y Jan 25 '22 at 20:36
172

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.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
stormlifter
  • 3,781
  • 3
  • 17
  • 20
  • 15
    You would still need to wrap it in a try... except ObjectDoesNotExist, which is like the original third option but with slicing. – Danny W. Adair Mar 09 '12 at 02:55
  • 1
    What's the point of setting a LIMIT if you're gonna call get() in the end ? Let the ORM and the SQL compiler decide what's best for it's backend (for example, on Oracle Django emulates LIMIT, so it will hurt instead of helping). – lqc Jul 03 '12 at 07:55
  • I used this answer without the trailing .get(). If a list is returned I then return the first element of the list. – Keith John Hutchison Jun 06 '13 at 01:10
  • what's the different of having `Entry.objects.all()[0]` ?? – James Lin Jun 16 '13 at 19:59
  • @JamesLin: There is none: `Entry.objects.all()[:1].get()` is the same as `Entry.objects.all()[0]` as both use `LIMIT 1` and return an object instead of a queryset. – jnns Sep 04 '13 at 13:06
  • 18
    @JamesLin The difference is that [:1].get() raises DoesNotExist, while [0] raises IndexError. – Ropez Sep 06 '13 at 05:42
55

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()

levi
  • 22,001
  • 7
  • 73
  • 74
  • 1
    This causes a LIMIT 1 in the SQL and I've seen claims that it can make the query slower -- although I'd like to see that substantiated: If the query only returns one item, why should the LIMIT 1 really affect performance? So I think the above answer is fine, but would love to see evidence confirming. – rrauenza Jun 29 '18 at 16:17
  • I wouldn't say "better". It really depends on your expectations. – trigras Apr 09 '20 at 15:12
  • Isn't `YourModel.objects.first()` enough, without the `.all()`? – Adam Jagosz Apr 11 '23 at 06:57
48
r = list(qs[:1])
if r:
  return r[0]
return None
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 1
    If you turn on tracing I'm pretty sure you'll even see this add `LIMIT 1` to the query, and I don't know that you can do any better than this. However, internally `__nonzero__` in `QuerySet` is implemented as `try: iter(self).next() except StopIteration: return false...` so it doesn't escape the exception. – Ben Jackson Feb 26 '11 at 00:00
  • @Ben: `QuerySet.__nonzero__()` is never called since the `QuerySet` is converted to a `list` before checking for trueness. Other exceptions may still occur however. – Ignacio Vazquez-Abrams Feb 26 '11 at 00:07
  • @Aron: That can generate a `StopIteration` exception. – Ignacio Vazquez-Abrams Apr 19 '12 at 01:21
  • converting to list === call ``__iter__`` to get a new iterator object and call it's ``next`` method until ``StopIteration`` is thrown. So definitively there is gonna be an exception somewhere ;) – lqc Jul 03 '12 at 05:35
  • Is this a necessery to make a slice first and after that take a first item? – Serhii Holinei Aug 30 '13 at 14:29
  • @goliney: Yes. The slice of the query set adds a `LIMIT` clause to the SQL query. – Ignacio Vazquez-Abrams Aug 30 '13 at 21:17
  • 14
    This answer is now outdated, take a look at @cod3monk3y answer for Django 1.6+ – ValAyal Sep 26 '14 at 14:48
8

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.

Nick Cuevas
  • 1,474
  • 15
  • 10
7

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()
Nikolay Fominyh
  • 8,946
  • 8
  • 66
  • 102
  • Call objects = ManagerWithFirstQuery as objects = ManagerWithFirstQuery() - DONT FORGET PARENTHESES - anyway, you helped me so +1 – Kamil Sep 05 '13 at 23:19
4

It can be like this

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

or

obj = model.objects.latest('id')
Naftali
  • 144,921
  • 39
  • 244
  • 303
Nauman Tariq
  • 1,350
  • 1
  • 14
  • 7
3

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

if qs.exists():
    return qs[0]
return None
Ari
  • 3,101
  • 2
  • 27
  • 49
  • 2
    Except, if I understand so correctly, idiomatic Python typically uses an _Easier to Ask for Forgiveness than Permission_ ([EAFP](https://docs.python.org/2/glossary.html#term-eafp)) approach rather than a _Look Before You Leap_ approach. – BigSmoke Jan 12 '16 at 12:48
  • EAFP is not just a style recommendation, it has reasons (for example, checking before opening a file does not prevent errors). Here I think the relevant consideration is that exists + get item cause two database queries, which may be undesirable depending on the project and view. – merwok Sep 18 '18 at 20:29
0

You can get the first object with these ways as shown below:

MyModel.objects.first()
MyModel.objects.all().first()
MyModel.objects.all()[0]
MyModel.objects.filter().first()
MyModel.objects.filter()[0]

In addition, you can get the last object with these ways as shown below:

MyModel.objects.last()
MyModel.objects.all().last()
MyModel.objects.filter().last()
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129