387

When I ask the model manager to get an object, it raises DoesNotExist when there is no matching object.

go = Content.objects.get(name="baby")

Instead of DoesNotExist, how can I have go be None instead?

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080

24 Answers24

508

There is no 'built in' way to do this. Django will raise the DoesNotExist exception every time. The idiomatic way to handle this in python is to wrap it in a try catch:

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

What I did do, is to subclass models.Manager, create a safe_get like the code above and use that manager for my models. That way you can write: SomeModel.objects.safe_get(foo='bar').

bignose
  • 30,281
  • 14
  • 77
  • 110
Arthur Debert
  • 10,237
  • 5
  • 26
  • 21
  • 10
    Nice use of SomeModel.DoesNotExist instead of importing the exception, too. – supermitch Mar 31 '14 at 18:37
  • 303
    This solution is four lines long. For me this is too much. With django 1.6 you can use `SomeModel.objects.filter(foo='bar').first()` this returns the first match, or None. It does not fail if there are several instances like `queryset.get()` – guettli Apr 14 '14 at 13:21
  • 30
    I think it is bad style to overuse exceptions for handling default cases. Yes, "it is easier to ask for forgiveness than for permission". But an exception should still be used, in my eyes, for exceptions. – Konstantin Schubert Apr 28 '15 at 20:57
  • Nice solution, I typically add this code into a base model and have that be the parent class of all my models. @staticmethod def getById( id ): .... – Luke Dupin Jul 11 '15 at 22:45
  • I regret that I can not comment on comments: @guettli: 4 lines can be easy placed into function/method and have 1 line usage with nice name meaning. – Sławomir Lenart May 06 '16 at 15:34
  • @ups I don't understand what you want to say to me. Have you read my whole comment? There is already a method in Django since version 1.6: `queryset.first()` – guettli May 09 '16 at 06:49
  • 12
    Explicit is better than implicit. Unless there's a performance reason to use `filter().first()` I think the exception is the way to go. – christianbundy Aug 05 '16 at 20:39
  • if you want a one-liner, just make a function that encapsulates this behaviour? – bryanph Oct 18 '16 at 11:43
  • 1
    @guettli or just use `SomeModel.objects.filter(foo='bar').exists()` if all you want is to check for existence – gdvalderrama Sep 22 '17 at 08:21
  • 11
    Using first() is only a good idea if you don't care when there are multiples. Otherwise, this solution is superior, because it will still throw an Exception if you unexpectedly find multiple objects, which is normally what you would want to happen in that case. – rossdavidh Mar 19 '18 at 16:10
  • 2
    This is to be prefered over `.first()` because `.first()` adds an (sql) `order by` to the query so this solution is expected to be faster. – Elmex80s Oct 11 '19 at 14:30
  • 1
    The highly upvoted argument of "this is four lines long" is not a valid one. Just wrap it in a method and it will become a one liner. As it has been said, using `first()` can lead to bugs with multiple cases and is slower. – David Dahan Nov 02 '22 at 11:43
  • Instructing a code to catch an exception that is likely to happen, doesn't seem a good practice, at all. – ivanleoncz Aug 07 '23 at 18:26
315

Since django 1.6 you can use first() method like so:

Content.objects.filter(name="baby").first()
Anupam
  • 14,950
  • 19
  • 67
  • 94
FeroxTL
  • 4,718
  • 3
  • 16
  • 14
  • 51
    In this case, no Error is raised if there is more than one match. – Konstantin Schubert Apr 28 '15 at 20:54
  • 2
    I like this solution, because using `objecst.get()` infers we know there's either one or none in the database. – emptyflash Aug 03 '15 at 15:22
  • 9
    'FeroxTL' you need to credit @guettli for this answer, as he commented this on the accepted answer a year prior to your post. – colm.anseo May 12 '16 at 14:15
  • 9
    @colminator I'd rather say guettli should learn that a new answer does not belong as a comment if he wants to raise his stackoverflow reputaiton :) FeroxTL should get points for making something hidden as a comment more clear as an answer. Your comment is credit enough for guettli I think and should not be added to the answer if that was your suggestion. – Joakim Sep 23 '16 at 13:21
  • 4
    @Joakim I have no problem with posting a new "answer" - just to give credit where it is due :-) – colm.anseo Oct 04 '16 at 14:39
  • 5
    What about performance of this approach comparing to accepted answer? – MaxCore Jun 26 '18 at 12:03
  • 4
    @MaxCore I think you are right. `.first()` adds an `order by` to the query and will therefore be slower. – Elmex80s Oct 11 '19 at 14:35
  • 1
    How much slower would an `order_by()` be when you are expected to have a single object though? I am not sure it would make much difference. – BoobyTrap Apr 16 '20 at 08:18
  • 3
    I just profiled a loop of 600 iterations. By replacing .first() with get, execution time for the loop is now x3 faster (postgres), and a lot of what's left is profiling overhead. get() is a *lot* faster. – Tim Richardson Nov 25 '20 at 08:30
  • 1
    In addition to performance and multiple matches risk, `first()` is just harder to understand that `get_or_none()` for someone that would read your code. This should matter. – David Dahan Nov 02 '22 at 12:18
58

You can create a generic function for this.

def get_or_none(classmodel, **kwargs):
    try:
        return classmodel.objects.get(**kwargs)
    except classmodel.DoesNotExist:
        return None

Use this like below:

go = get_or_none(Content,name="baby")

go will be None if no entry matches else will return the Content entry.

Note:It will raises exception MultipleObjectsReturned if more than one entry returned for name="baby".

You should handle it on the data model to avoid this kind of error but you may prefer to log it at run time like this:

def get_or_none(classmodel, **kwargs):
    try:
        return classmodel.objects.get(**kwargs)
    except classmodel.MultipleObjectsReturned as e:
        print('ERR====>', e)

    except classmodel.DoesNotExist:
        return None
Olvin Roght
  • 7,677
  • 2
  • 16
  • 35
Ranju R
  • 2,407
  • 21
  • 16
  • Great! if it is a client side error: from django.shortcuts import get_object_or_404 else it is a server side problem so get_or_none is the best. – F.Tamy Sep 02 '20 at 10:29
51

From django docs

get() raises a DoesNotExist exception if an object is not found for the given parameters. This exception is also an attribute of the model class. The DoesNotExist exception inherits from django.core.exceptions.ObjectDoesNotExist

You can catch the exception and assign None to go.

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None
rohan
  • 1,606
  • 12
  • 21
Amarghosh
  • 58,710
  • 11
  • 92
  • 121
26

You can do it this way:

go  = Content.objects.filter(name="baby").first()

Now go variable could be either the object you want or None

Ref: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first

Gaurav
  • 1,053
  • 10
  • 11
22

To make things easier, here is a snippet of the code I wrote, based on inputs from the wonderful replies here:

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

And then in your model:

class MyModel(models.Model):
    objects = MyManager()

That's it. Now you have MyModel.objects.get() as well as MyModel.objetcs.get_or_none()

Moti Radomski
  • 275
  • 2
  • 8
18

you could use exists with a filter:

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

just an alternative for if you only want to know if it exists

Ryan Saxe
  • 17,123
  • 23
  • 80
  • 128
  • 4
    That would cause an extra database call when exists. Not a good idea – Christoffer Nov 25 '15 at 16:36
  • @Christoffer not sure why would that be an extra db call. As per the [docs](https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated): `Note: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().` – Anupam May 01 '18 at 09:03
  • 2
    @Christoffer I think you are right. I now read the question again and the OP actually wants the actual object to be returned. So `exists()` will be used with `if` clause before fetching the object hence causing a double hit to the db. I'll still keep the comment around in case it helps someone else. – Anupam May 01 '18 at 09:07
10

It's one of those annoying functions that you might not want to re-implement:

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
mknecht
  • 1,205
  • 11
  • 20
  • 1
    I checked the code of `get_object_or_None` but found that it still raise `MultipleObjectsReturned` if more than one object. So, user might consider surrounding with a `try-except` (which the function itself has a `try-except` already). – John Pang Jan 23 '19 at 16:56
8

Maybe is better you use:

User.objects.filter(username=admin_username).exists()
Ch3steR
  • 20,090
  • 4
  • 28
  • 58
  • 2
    The problem with this is if the User is deleted between this line and the line where it's actually retrieved. – Neil Sep 26 '21 at 11:51
7

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:
    # Do something
else:
    # This post doesn't exist
Mahammad Adil Azeem
  • 9,112
  • 13
  • 57
  • 84
  • 1
    I really like this solution, but wasn't able to get it to work as is when using python 3.6. Wanted to leave a note that modifying the return in the ContentManager to `return self.get(**kwargs)` got it to work for me. Not to say anything's wrong with the answer, just a tip for anyone trying to use it with later versions (or with whatever else kept it from working for me). – skagzilla Oct 04 '17 at 15:10
5

If you want a simple one-line solution that doesn't involve exception handling, conditional statements or a requirement of Django 1.6+, do this instead:

x = next(iter(SomeModel.objects.filter(foo='bar')), None)
blhsing
  • 91,368
  • 6
  • 71
  • 106
5

I think it isn't bad idea to use get_object_or_404()

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

This example is equivalent to:

from django.http import Http404

def my_view(request):
    try:
        my_object = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

You can read more about get_object_or_404() in django online documentation.

Mohammad Reza
  • 693
  • 9
  • 16
3

From django 1.7 onwards you can do like:

class MyQuerySet(models.QuerySet):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None


class MyBaseModel(models.Model):

    objects = MyQuerySet.as_manager()


class MyModel(MyBaseModel):
    ...

class AnotherMyModel(MyBaseModel):
    ...

The advantage of "MyQuerySet.as_manager()" is that both of the following will work:

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
Vinayak Kaniyarakkal
  • 1,110
  • 17
  • 23
2

This is a copycat from Django's get_object_or_404 except that the method returns None. This is extremely useful when we have to use only() query to retreive certain fields only. This method can accept a model or a queryset.

from django.shortcuts import _get_queryset


def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or return None if object
    does not exist.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset = _get_queryset(klass)
    if not hasattr(queryset, 'get'):
        klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None
Sandeep Balagopal
  • 1,943
  • 18
  • 28
2

I use Django 2.2.16. And this is how I solve this problem:

from typing import Any

from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models.base import ModelBase
from django.db.models.manager import Manager


class SManager(Manager):
    def get_if_exist(self, *args: Any, **kwargs: Any):
        try:
            return self.get(*args, **kwargs)
        except ObjectDoesNotExist:
            return None


class SModelBase(ModelBase):
    def _prepare(cls):
        manager = SManager()
        manager.auto_created = True
        cls.add_to_class("objects", manager)

        super()._prepare()

    class Meta:
        abstract = True


class SModel(models.Model, metaclass=SModelBase):
    managers = False

    class Meta:
        abstract = True

And after that, in every models, you just need to import in:

from custom.models import SModel


class SUser(SModel):
    pass

And in views, you can call like this:

SUser.objects.get_if_exist(id=1)
Sihc
  • 59
  • 6
1

Here's a variation on the helper function that allows you to optionally pass in a QuerySet instance, in case you want to get the unique object (if present) from a queryset other than the model's all objects queryset (e.g. from a subset of child items belonging to a parent instance):

def get_unique_or_none(model, queryset=None, **kwargs):
    """
        Performs the query on the specified `queryset`
        (defaulting to the `all` queryset of the `model`'s default manager)
        and returns the unique object matching the given
        keyword arguments.  Returns `None` if no match is found.
        Throws a `model.MultipleObjectsReturned` exception
        if more than one match is found.
    """
    if queryset is None:
        queryset = model.objects.all()
    try:
        return queryset.get(**kwargs)
    except model.DoesNotExist:
        return None

This can be used in two ways, e.g.:

  1. obj = get_unique_or_none(Model, **kwargs) as previosuly discussed
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)
Gary
  • 4,426
  • 1
  • 22
  • 19
1

Without exception:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

Using an exception:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

There is a bit of an argument about when one should use an exception in python. On the one hand, "it is easier to ask for forgiveness than for permission". While I agree with this, I believe that an exception should remain, well, the exception, and the "ideal case" should run without hitting one.

Konstantin Schubert
  • 3,242
  • 1
  • 31
  • 46
1

We can use Django builtin exception which attached to the models named as .DoesNotExist. So, we don't have to import ObjectDoesNotExist exception.

Instead doing:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

We can do this:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None
zakiakhmad
  • 783
  • 6
  • 10
1

I was facing with the same problem too. It's hard to write and read try-except for each time when you want to get an element from your model as in @Arthur Debert's answer. So, my solution is to create an Getter class which is inherited by the models:

class Getter:
    @classmethod
    def try_to_get(cls, *args, **kwargs):
        try:
            return cls.objects.get(**kwargs)
        except Exception as e:
            return None

class MyActualModel(models.Model, Getter):
    pk_id = models.AutoField(primary_key=True)
    ...

In this way, I can get the actual element of MyActualModel or None:

MyActualModel.try_to_get(pk_id=1)
Ersel Er
  • 731
  • 6
  • 22
1

Simple way:

if query.exists(): do something....

or

if query.exists() is False: do something...

Den se
  • 11
  • 1
0

I prefer this method without using exceptions. It also handles multiple objects as well as no objects.

go_list = Content.objects.filter(name="baby")
if (len(go_list) == 1):
    go = go_list[0]
else:
    go = None # optionally do other things if there are multiple objects / no objects.
Uri
  • 2,992
  • 8
  • 43
  • 86
0

What if something like this?

go = (Content.objects.filter(name="value") or [None])[0]
Vova
  • 3,117
  • 2
  • 15
  • 23
0

As it has been mentioned in other answers, you can use

filter(**kwargs).first()

The issue with this method is it never throws a MultipleObjectsReturned error if the query returns multiple objects. This may not always be desirable. Assume you have the following database table

id first_name last_name
1 Keenen Wayans
2 Marlon Wayans
3 Shawn Wayans

Person.objects.filter(last_name="Wayans").first()

will always return Keenen Wayans. The user will never know there are other 'Wayans'

If you don't like this, below is my reimplementation of Django's shortcut method get_object_or_404. If no object is found, it returns None, but if the query returns multiple objects, it throws a MultipleObjectsReturned exception. I would rather handle MultipleObjectsReturned exception, instead of returning a bogus value to the user.

In a separate file called shortcuts.py, create a method called get_object_or_none.

def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or returns None if the object
    does not exist instead of throwing an exception.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset =  klass._default_manager.all() if hasattr(klass, "_default_manager") else klass
    if not hasattr(queryset, "get"):
        klass__name = (
            klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        )
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None

Then in views.py

from myapp.shortcuts import get_object_or_none

person = get_object_or_none(Person, first_name='Shawn', last_name='Wayans')
#person is 'Shawn Wayans'

person = get_object_or_none(Person, last_name='Hendrix')
#person is None as database has no person with last name 'Hendrix'

person = get_object_or_none(Person, last_name='Wayans')
#throws 'MultipleObjectsReturned' error since multiple persons returned.
Moses Machua
  • 11,245
  • 3
  • 36
  • 50
-2

How about a slice? It will parse to a limit 1.

go = Content.objects.filter(name="baby")[0]
j__carlson
  • 1,346
  • 3
  • 12
  • 20