Understanding the difference between .get and .first in Django
Posted on Fri 21 May 2021 in Better Django
When retrieving a single record in Django, the ORM offers two possible methods: .get()
and .first()
. Let's learn how each one works and when to use them.
Get
Django's most basic way to retrieve a single record is the .get() method. It is used as follows:
burger = Menu.objects.get(name="Beef Burger")
Quick facts about .get()
:
-
It retrieves only one record.
-
If no record exists that meets the given criteria, it raises a
DoesNotExist
exception. -
If more than one record with the given criteria exists, it raises a
MultipleObjectsReturned
exception.
First
A different way to get one record is to use .first() on a QuerySet. For example:
burger = Menu.objects.filter(name="Beef Burger").first()
Quick facts about .first()
:
-
It will return the first item of a recordset.
-
If no record exists, it returns
None
.
Handling unexpected situations
Get
If Django's ORM can't return one record (i.e. there is either no record, or there are more than one), you have to catch the relevant exception. For example:
try:
return Menu.objects.get(name="Beef Burger")
except Menu.DoesNotExist:
raise Exception("Could not find a burger")
except Menu.MultipleObjectsReturned:
raise Exception("Found too many burgers")
First
When using .first()
, the only unexpected situation is if no record was present in the QuerySet. Since it's not a problem if multiple records exist that meet the criteria, we have to deal only with the one case. For example:
burger = Menu.objects.filter(name="Beef Burger").first()
if not burger:
raise Exception("Could not find a burger")
return burger
Subtle differences
Did you notice the differences between the two above? For .get()
we have to deal with more "error" scenarios. But why is that? The .get()
method expects no more and no less than one record to be returned, whereas .first()
assumes there might be multiple records, and it returns the first. So we have a fundamental difference in the assumptions each of these makes.
Should we care? Does it really make a difference which of the two methods we use if we get the same result?
I used to think it didn't matter, but I was wrong.
-
Using
.get()
might point to a problem with our Model that we are unaware of. If we are expecting only one record to be returned, but we get back multiple, we have failed to make sure that one of the fields (or multiple ones) enforce a unique constraint. Handling this at a database layer is crucial. If we had used.first()
, and had not added a unique constraint, we might have never noticed if there were multiple records where we are expecting only one. -
What about readability? I never thought about how the code reads in each case, and what it tells the reader about the data models. When you use
.get()
, the reader will (or should) know immediately that the record should be unique for the fields you filtered on. However, if you use.first()
, it implies to the reader that multiple records might exist, but you only care about the first one.
When to use which
Get
Use .get()
when you are expecting only one (or a maximum of one) record to be returned. The code makes your intentions clear, and there's less room for undetected errors.
First
Use .first()
when you know there might be multiple records (and that's ok), but you only care about the first. In most cases you should also have some order defined on the model (or specified on your query), otherwise the first record might be random. In some cases you might not even care about the order, but just be aware of it.
Gotchas
-
If you use
.first()
, you run the risk of using theNone
(in the case of no record found) as a different return data type to signify the fact that nothing was found. It's not very likely, but I've caught myself doing this. I wrote about that trap here. -
The SQL query generated by
.get()
does not have a limit. So if you have multiple records, before theMultipleObjectsReturned
exception is raised, your database will return all the records matching that criteria. But using.first()
adds aLIMIT 1
to your query, so it will only ever return one record. Be careful which one you choose.
Summary
If you query the database and only ever expect one record (maximum) to be returned, don't use .first()
, but rather .get()
instead. You'll thank yourself later.