DEV Community

Cover image for Prefer Model.DoesNotExist over ObjectDoesNotExist in Django
Anže Pečar
Anže Pečar

Posted on • Edited on

Prefer Model.DoesNotExist over ObjectDoesNotExist in Django

In Django it's very common to see code like this:

from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from myproject.apps.teams.models import Team

try:
    user = User.objects.get(pk=1337)
    phone = Team.objects.get(pk=1337)
except ObjectDoesNotExist:
    logging.error("User or Team does not exist")
Enter fullscreen mode Exit fullscreen mode

The code will try to fetch an auth user and a team from the database and if either of those does not exist, we'll just log an error. This is so common that I never thought twice when seeing or writing it.

But it turns out that there is a slightly better way to accomplish the same result. I didn't know this before recently, but the Django Model class has its own DoesNotExist exception! We can use it like this:

from django.contrib.auth.models import User
from myproject.apps.teams.models import Team

try:
    user = User.objects.get(pk=1337)
    phone = Phone.objects.get(pk=1337)
except User.DoesNotExist:
    logging.error("User does not exist")
except Phone.DoesNotExist:
    logging.error("Phone does not exist")
Enter fullscreen mode Exit fullscreen mode

This is better for a couple of reasons:

  1. The two except blocks are now more explicit and will only catch exceptions when the specific model does not exist.
  2. This is minor, but you don't have to worry if ObjectDoesNotExist is already imported in the current scope or not.

MultipleObjectsReturned exception is implemented the exact same way and if you are curious to learn how Django implements a new Exception type for every subclass that you make, you can check out the source code here.

My day job codebase currently has 231 occurrences of except ObjectDoesNotExist. I'll slowly start replacing them with model.DoesNotExist 😊

Top comments (4)

Collapse
 
itachiuchiha profile image
Itachi Uchiha • Edited

I think that explicit programming is very useful. Developers can understand easily what happens where. I added the UserPhones model to your code (I know this code isn't right. This just an example).

from django.contrib.auth.models import User, UserPhones

try:
    user = User.objects.get(pk=1337) # User found
    phones = UserPhones.objects.get(user_id=1337)
except user.DoesNotExist:
    logging.error("User does not exist")
except phones.DoesNotExist:
    logging.error("User does not have phone numbers")

In the above code, we can catch exceptions easily.

ObjectDoesNotExist catches DoesNotExist exceptions for all models.

DoesNotExist raised by Django's ORM and it is a subclass of the ObjectDoesNotExist

There is also EmptyResultSet exception, but nobody recommends this exception.

Collapse
 
anze3db profile image
Anže Pečar

You are right. Thanks for the suggestion. I have updated my blog post to include a similar example 👍

Collapse
 
thorvald profile image
Japanesen

Strangely enough this way of using exceptions with DoesNotExist makes Pylance think the second clause is unreachable code. Any idea why?

Collapse
 
aemiej profile image
Aemie Jariwala

I have added my ip address to the allowed host to be accessible through other computer, however this doesn't occur.