Fastest way to get the first object from a queryset in django?
Question:
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?
Answers:
r = list(qs[:1])
if r:
return r[0]
return None
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.
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()
You should use django methods, like exists. Its there for you to use it.
if qs.exists():
return qs[0]
return None
It can be like this
obj = model.objects.filter(id=emp_id)[0]
or
obj = model.objects.latest('id')
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.
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()
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.
I discovered something that I think might be helpful to anyone who comes across this page.
When using prefetch_related(), calling .first()
would actually make another query on the database (with some OFFSET
and LIMIT
clauses), whereas .all()
just returns the data the queryset has already prefetched.
Given that, I would opt for this method to pick the first item when using prefetch_related
from a queryset in Django:
>>> Entry.objects.all()[0]
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?
r = list(qs[:1])
if r:
return r[0]
return None
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.
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()
You should use django methods, like exists. Its there for you to use it.
if qs.exists():
return qs[0]
return None
It can be like this
obj = model.objects.filter(id=emp_id)[0]
or
obj = model.objects.latest('id')
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.
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()
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.
I discovered something that I think might be helpful to anyone who comes across this page.
When using prefetch_related(), calling .first()
would actually make another query on the database (with some OFFSET
and LIMIT
clauses), whereas .all()
just returns the data the queryset has already prefetched.
Given that, I would opt for this method to pick the first item when using prefetch_related
from a queryset in Django:
>>> Entry.objects.all()[0]