68

Whenever I do this:

thepost = Content.objects.get(name="test")

It always throws an error when nothing is found. How do I handle it?

TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • See also [QuerySet.get](http://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.QuerySet.get) – Davor Lucic Dec 04 '10 at 12:00
  • 6
    That's the way it is supposed to work. What's your question? How to write a `try` statement? – S.Lott Dec 04 '10 at 12:37

8 Answers8

120
try:
    thepost = Content.objects.get(name="test")
except Content.DoesNotExist:
    thepost = None

Use the model DoesNotExist exception

Ozzy
  • 8,244
  • 7
  • 55
  • 95
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 9
    In this case, the exception is `Content.DoesNotExist`, and this code structure is useful if you want to continue even if the object cannot be found (i.e. not throw a 404 error as in my example). – Rob Golding Dec 04 '10 at 11:04
37

Often, it is more useful to use the Django shortcut function get_object_or_404 instead of the API directly:

from django.shortcuts import get_object_or_404

thepost = get_object_or_404(Content, name='test')

Fairly obviously, this will throw a 404 error if the object cannot be found, and your code will continue if it is successful.

Flimm
  • 136,138
  • 45
  • 251
  • 267
Rob Golding
  • 3,502
  • 5
  • 26
  • 28
18

You can also catch a generic DoesNotExist. As per the docs at http://docs.djangoproject.com/en/dev/ref/models/querysets/

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print "Either the entry or blog doesn't exist."
zobbo
  • 236
  • 1
  • 3
12

Another way of writing:

try:
    thepost = Content.objects.get(name="test")
except Content.DoesNotExist:
    thepost = None

is simply:

thepost = Content.objects.filter(name="test").first()

Note that the two are not strictly the same. Manager method get will raise not only an exception in the case there's no record you're querying for but also when multiple records are found. Using first when there are more than one record might fail your business logic silently by returning the first record.

Rafael Valverde
  • 301
  • 3
  • 2
  • Just want to chime in - I've found it's almost always better to use `.get` instead of `.first` because as you said it can lead to unexpected results. The only time I use `.first` is if I use a generic foreign key and put validation in place to make sure there's only ever max one item. – inostia Oct 27 '20 at 00:58
8

Catch the exception

try:
    thepost = Content.objects.get(name="test")
except Content.DoesNotExist:
    thepost = None

alternatively you can filter, which will return a empty list if nothing matches

posts = Content.objects.filter(name="test")
if posts:
    # do something with posts[0] and see if you want to raise error if post > 1
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • why allocate an empty Queryset when get() will just raise an exception? The try/catch first approach is far better – ppetrid Nov 17 '12 at 01:44
  • Normally, using exceptions for normal 'workflow' is not a good practice. In this example it's fairly easy to understand, but if you start to use exceptions to control an application, it's going to be hard to maintain/debug. – mrmuggles Nov 03 '15 at 16:07
  • @mrmuggles I disagree, Exception could be a very good way to structure your code, e..g `for plugin in plugins; try; processed_text = plugin.process(text) ; except Exception1,: do something Exception 2; do something` there is no easier alternate to this, and in this specific case it absolutely makes perfect sense, would you dod dict.has_key before accessing item or just access item and catch KeyError? – Anurag Uniyal Nov 03 '15 at 17:26
  • Using exception to structure workflow is an anti-pattern IMO. Here's a discussion about that: http://programmers.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why Yes, you can save a couple of line of codes, but it doesn't make it a good solution for maintenance. I would check if the item exists, and in fact I would probably wrap this code in a method anyway. – mrmuggles Nov 03 '15 at 19:39
  • i think that opinion is not specific to python, I liked the comment on the question 'No. In Python, using exceptions as control flow is considered "pythonic"', python itself uses Exception to tell end of generators etc, see this http://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python and http://www.drmaciver.com/2009/03/exceptions-for-control-flow-considered-perfectly-acceptable-thanks-very-much/ , in the end I will say it is moe pythonic, may be not suited for C++ but for python yes. – Anurag Uniyal Nov 04 '15 at 17:35
  • 2
    @mrmuggles The Python community embraces EAFP, aka Easier to Ask for Forgiveness than Permission. This style is widely embraced throughout the standard library and the big must-use Python third party libraries. Here's a random blog post I googled on the subject: https://www.jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/ – Dustin Wyatt Nov 25 '15 at 18:57
4

Handling exceptions at different points in your views could really be cumbersome..What about defining a custom Model Manager, in the models.py file, like

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

and then including it in the content Model class

class Content(model.Model):
    ...
    objects = ContentManager()

In this way it can be easily dealt in the views i.e.

post = Content.objects.get_nicely(pk = 1)
if post != None:
    # Do something
else:
    # This post doesn't exist
Mahammad Adil Azeem
  • 9,112
  • 13
  • 57
  • 84
4

There are essentially two ways you can do this. The first approach is more verbose as it doesn't use any shortcuts:

from django.http import Http404
from .models import Content    


try:
    thepost = Content.objects.get(name="test")
except Content.DoesNotExist:
    raise Http404("Content does not exist")

Now since this sort of operation (i.e. get an object or raise 404 if it does not exist) is quite common in Web Development, Django offers a shortcut called get_object_or_404 that will get() the requested object or raise Http404 in case it is not found:

from django.shortcuts import get_object_or_404
from .models import Content


thepost = get_object_or_404(Content, name="test")

Likewise for lists of objects, you can use get_list_or_404() function that makes use of filter() instead of get() and in case the returned list is empty, Http404 will be raised.

Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
3

Raising a Http404 exception works great:

from django.http import Http404

def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render_to_response('polls/detail.html', {'poll': p})
Banjer
  • 8,118
  • 5
  • 46
  • 61
  • 1
    This works only if the Poll object represents the whole view. If you're just trying to query for an object that will land in the middle of a page, you don't want to return 404 for the whole view. – shacker Feb 17 '16 at 18:57