Django QuerySets: A simple pattern to fetch single model instances

Quite frequently in Django you’ll want to retrieve a single object for a particular model from the database. Django recommends the following pattern for this:

    sub = Subscription.objects.get(user=request.user)
    # do something
except Subscription.DoesNotExist:
    # handle exception

This is quite a lot of code for a relatively simple operation, and things can quickly get messy if you need to fetch two or more different model instances to do something. Perhaps you’re more comfortable using filter() but you still need to slice the QuerySet to return a single object instead of a QuerySet:

sub = Subscription.objects.filter(user=request.user)[:1]

Another problem with this second approach is that if there are no matches (an empty QuerySet) then Python will raise an IndexError, so you still need to place this within a try/catch block.

Something about the above pattern was bugging me. It felt inelegant. So I went in search of alternatives and came across first() , which basically returns the first object in the QuerySet or None if there are no matches. This felt so much cleaner to use for cases when you want to do something with that object only if it exists:

sub = Subscription.object.filter(user=request.user).first()
if subscription:
    # do something

If you don’t need to retrieve the object from the database and just want to check if it exists then you can use exists() instead of first(). The former is more efficient and returns a Boolean result.

Finally, if you want to automatically raise a 404 error when an object does not exist you can always use Django’s get_object_or_404 shortcut:

from django.shortcuts import get_object_or_404
from subscriptions.models import Subscription

def subscription_detail(request):
    sub = get_object_or_404(Subscription, user=request.user)
    return render(request, 'template.html', {'sub': sub})

I’ve been using first() for quite a while now and find it to be a convenient pattern to keep code structure clean and readable. Hopefully you’ll find it useful too.