108

Is there any Django function which will let me get an object form the database, or None if nothing matches?

Right now I'm using something like:

foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None

But that's not very clear, and it's messy to have everywhere.

David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • 2
    You know you can just use foo = foo[0] if foo else None – Visgean Skeloru Oct 10 '12 at 21:35
  • 2
    Python has a ternary operator, you don't have to use boolean operators. Also, [`len(foo)` is bad](https://docs.djangoproject.com/en/dev/ref/models/querysets/): "*Note: Don’t use len() on QuerySets if all you want to do is determine the number of records in the set. It’s much more efficient to handle a count at the database level, using SQL’s SELECT COUNT(), and Django provides a count() method for precisely this reason.*". Rewritten: `foo = foo[0] if foo.exists() else None` – mustafa.0x Sep 06 '13 at 11:45
  • 1
    possible duplicate of [In Django, how do I objects.get, but return None when nothing is found?](http://stackoverflow.com/questions/3090302/in-django-how-do-i-objects-get-but-return-none-when-nothing-is-found) – SleepyCal Mar 13 '14 at 14:02
  • 1
    @mustafa.0x I don't think that applies here. Here, checking for count would run another query. Then we'd have query again to get the actual data. This is a case where filtering and then counting would make more sense... but not more sense than filtering and calling `first()` :P – MicronXD Aug 03 '14 at 23:01
  • http://stackoverflow.com/a/20674112/1259116 – Ranju R Sep 19 '16 at 11:55

8 Answers8

149

There are two ways to do this;

try:
    foo = Foo.objects.get(bar=baz)
except model.DoesNotExist:
    foo = None

Or you can use a wrapper:

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

Call it like this

foo = get_or_none(Foo, baz=bar)
I. J. Kennedy
  • 24,725
  • 16
  • 62
  • 87
Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
  • 9
    I don't like the fact that it's using exceptions for functionality - this is a bad practice in most languages. How is that in python? – pcv Jan 07 '11 at 12:41
  • 20
    That's how Python iteration works (raises a StopIteration), and that's a core part of the language. I'm not a huge fan of the try/except functionality either, but it appears to be considered Pythonic. – Matt Luongo Jan 06 '12 at 15:18
  • 5
    @pcv: there is no reliable conventional wisdom about "most" languages. I suppose you think about some specific language you happen to use, but be careful with such quantifiers. Exceptions can be as slow (or "fast enough" for that matter), as anything in Python. Going back to the question - it is a shortcoming of the framework, that they didn't provide any `queryset` method to do this before 1.6! But as for this construct - it is just clumsy. It's not "evil" because some tutorial author of your favourite language said so. Try google: "ask forgiveness rather than permission". – Tomasz Gandor Aug 31 '13 at 06:44
  • 1
    @TomaszGandor: Yeah, it is clumsy and difficult to read, whatever your favorite language tutorial says. When I see an exception I suppose that something non-standard is happening. Readability counts. But I agree when there's no other reasonable option, you should go for it and nothing bad will happen. – pcv Sep 05 '13 at 23:39
  • @pcv The `get` method is not supposed to return `None`, so an exception makes sense. You're supposed to use it in instances where you're sure the object will be there/the object is "supposed" to be there. Also using exceptions like this is considered 'okay' because, well, it makes sense. You want a `get` with a different contract, so you catch the exception and suppress it, which is the change you're after. – Dan Passaro Nov 19 '13 at 21:45
94

In Django 1.6 you can use the first() Queryset method. It returns the first object matched by the queryset, or None if there is no matching object.

Usage:

p = Article.objects.order_by('title', 'pub_date').first()
Cesar Canassa
  • 18,659
  • 11
  • 66
  • 69
  • 20
    This is not a good answer, if multiple objects are found then `MultipleObjectsReturned()` is not raised, as documented [here](https://docs.djangoproject.com/en/1.2/ref/exceptions/#multipleobjectsreturned). You can find some better answers [here](http://stackoverflow.com/questions/3090302/in-django-how-do-i-objects-get-but-return-none-when-nothing-is-found) – SleepyCal Mar 13 '14 at 13:59
  • 27
    @sleepycal I disagree. The `first()` works exactly as it suppose to. It returns the first object in query result and doesn't cares if multiple results are found. If you need to check for multiple objects returned you should use `.get()` instead. – Cesar Canassa Mar 13 '14 at 15:03
  • 7
    [edited] As the OP said, `.get()` is not suitable for his needs. The correct answer to this question can be found below by @kaapstorm, and is clearly the more suitable answer. Abusing `filter()` this way can lead to unexpected consequences, something which OP probably didn't realize (unless I'm missing something here) – SleepyCal Mar 13 '14 at 16:26
  • 1
    @sleepycal What unintended consequences arise from this approach? – HorseloverFat Apr 01 '14 at 11:21
  • 13
    If your database constraints do not correctly manage uniqueness across objects, then you can hit edge cases where your logic expects there to be only a single object (which is selected by first()), but in reality multiple objects exist. Using get() instead of first() gives you an extra layer of protection, by raising `MultipleObjectsReturned()`. If the result being returned is not expected to return multiple objects, then it should not be treated as such. There was a *long* debate about this [here](https://groups.google.com/forum/#!msg/django-developers/h50RLblVDU8/1fhPK_IcjZMJ) – SleepyCal Apr 01 '14 at 19:06
  • @sleepycal Thanks for the link. Would you say this is a perfectly acceptable approach if you are correctly enforcing uniqueness at the model/db constraint level? That seems to be the only objection. – HorseloverFat Apr 03 '14 at 12:37
  • 1
    That's a good question. If the database constraints are correctly applied, and you don't wish to have the extra protection of detecting multiple objects, then `first()` would be fine. It's also a matter of taste, if your logic assumes there is only one object, then the naming convention of calling `first()` feels wrong (imho). – SleepyCal Apr 03 '14 at 14:01
84

To add some sample code to sorki's answer (I'd add this as a comment, but this is my first post, and I don't have enough reputation to leave comments), I implemented a get_or_none custom manager like so:

from django.db import models

class GetOrNoneManager(models.Manager):
    """Adds get_or_none method to objects
    """
    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None

class Person(models.Model):
    name = models.CharField(max_length=255)
    objects = GetOrNoneManager()

And now I can do this:

bob_or_none = Person.objects.get_or_none(name='Bob')
kaapstorm
  • 1,101
  • 7
  • 8
14

You can also try to use django annoying (it has another useful functions!)

install it with:

pip install django-annoying

from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)
llazzaro
  • 3,970
  • 4
  • 33
  • 47
10

Give Foo its custom manager. It's pretty easy - just put your code into function in custom manager, set custom manager in your model and call it with Foo.objects.your_new_func(...).

If you need generic function (to use it on any model not just that with custom manager) write your own and place it somewhere on your python path and import, not messy any more.

sorki
  • 451
  • 2
  • 6
4

Whether doing it via a manager or generic function, you may also want to catch 'MultipleObjectsReturned' in the TRY statement, as the get() function will raise this if your kwargs retrieve more than one object.

Building on the generic function:

def get_unique_or_none(model, *args, **kwargs):
    try:
        return model.objects.get(*args, **kwargs)
    except (model.DoesNotExist, model.MultipleObjectsReturned), err:
        return None

and in the manager:

class GetUniqueOrNoneManager(models.Manager):
    """Adds get_unique_or_none method to objects
    """
    def get_unique_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except (self.model.DoesNotExist, self.model.MultipleObjectsReturned), err:
            return None
SleepyCal
  • 5,739
  • 5
  • 33
  • 47
emispowder
  • 1,787
  • 19
  • 21
0

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, *args, **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(*args, **kwargs)
    except model.DoesNotExist:
        return None

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

  1. obj = get_unique_or_none(Model, *args, **kwargs) as previosuly discussed
  2. obj = get_unique_or_none(Model, parent.children, *args, **kwargs)
SleepyCal
  • 5,739
  • 5
  • 33
  • 47
Gary
  • 4,426
  • 1
  • 22
  • 19
-3

I think that in most cases you can just use:

foo, created = Foo.objects.get_or_create(bar=baz)

Only if it is not critical that a new entry will be added in Foo table ( other columns will have the None/default values )

jrbedard
  • 3,662
  • 5
  • 30
  • 34
TomerP
  • 1
  • 1