How to handle "matching query does not exist" when getting an object
Asked Answered
A

4

8

When I want to select objects with a get() function like

personalProfile = World.objects.get(ID=personID)

If get function doesn't return find a value, a "matching query does not exist." error occurs.

If I don't need this error, I'll use try and except function

try:
   personalProfile = World.objects.get(ID=personID)
except:
   pass

But I think this is not the best way since I use

except:
      pass

Please recommend some idea or code sample to fight with this issue

Arianism answered 14/10, 2015 at 7:44 Comment(0)
I
15

That depends on what you want to do if it doesn't exist..

Theres get_object_or_404:

Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.

get_object_or_404(World, ID=personID)

Which is very close to the try except code you currently do.

Otherwise theres get_or_create:

personalProfile, created = World.objects.get_or_create(ID=personID)

Although, If you choose to continue with your current approach, at least make sure the except is localised to the correct error and then do something with that as necessary

try:
   personalProfile = World.objects.get(ID=personID)
except MyModel.DoesNotExist:
    raise Http404("No MyModel matches the given query.")

The above try/except handle is similar to what is found in the docs for get_object_or_404...

Incus answered 14/10, 2015 at 7:53 Comment(1)
Thanks for this help. I try this and getting error because I haven't import a Http404 Then fix this by "from django.http import Http404" and perfect!Arianism
D
4

A get_or_none() function has been proposed, multiple times now. The rejection notice is feature creep, which you might or might not agree with. The functionality is present --with slightly different semantics-- in the first() queryset method.

But first things first:

The manager throws World.DoesNotExist, a specialized subclass of ObjectDoesNotExist when a World object was not found:

try:
    personalProfile = World.objects.get(ID=personID)
except World.DoesNotExist:
    pass

There's also get_object_or_404() which raises a Http404 exception when the object was not found.

You can also roll your own get_or_none(). A possible implementation could be:

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

Note that this still raises MultipleObjectsReturned when more than one matching object is found. If you always want the first object regardless of any others, you can simplify using first(), which returns None when the queryset is empty:

def get_or_none(queryset, *args, **kwargs):
    return queryset.filter(*args, **kwargs).first()

Note however, for this to work reliably, you need a proper order for objects, because in the presence of multiple objects first() might be non-deterministic (it probably returns the first object from the database index used to filter the query and neither indexes not the underlying tables need be sorted or even have a repeatable order).

Use both, however, only when the use of the object to retrieve is strictly optional for the further program flow. When failure to retrieve an object is an error, use get_object_or_404(). When an object should be created when it does not exist, use get_or_create(). In those cases, both are better suited to simplify program flow.

Dissimilarity answered 14/10, 2015 at 7:56 Comment(7)
The first() method added in Django 1.6 is pretty similar to get_or_none. Note that you're not handling MultipleObjectsReturned in your method. The first() method simply takes the first element, and ignores the rest.Goosander
@Goosander Not handling MultipleObjectsReturned is quite intentional. However, yes, get_or_none() can be simplified to use first(). Have you checked how that affects the generated SQL queries?Dissimilarity
My point is that although the tickets you linked to were closed as won't fix, the feature was eventually added, but with a different name. MyModel.objects.filter(**kwargs).first() does the same thing get_object_or_none(MyModel, **kwargs), unless you really care about MultipleObjectsReturned (which I expect most of the time you don't). I haven't examined the SQL, but I expect it will be pretty similar, perhaps with some differences with limit.Goosander
@Goosander Hmm, I think that depends on what semantics you want. I actually do care about MultipleObjectsReturned. When there are multiple objects that match the filter and I want only one, first() might return any of them non-deterministically depending if the underlying queryset is sorted or not. If prefer django to bark at me loud and clear when that happens ;).Dissimilarity
Sure, I understand that handling MultipleObjectsReturned is important sometimes, which is why I mentioned it to begin with. In those cases, you need a try except block to handle it, and in that case I'd rather use get(), rather than a get_or_none() shortcut that handles one possible exception but not the other.Goosander
@Goosander We might have different use cases, then. I have get_or_none() in a few loader scripts mostly as a substitute for get_or_create() where the retrieval needs different arguments than the associated create (i.e. something like get_or_create_with_default_args). In those cases, no object is okay, but multiple objects is a fatal error. I also just checked and I actually use it twice in non-script code, so you are probably right and the use case is quite special.Dissimilarity
I think we're pretty much in agreement, you obviously know your use cases better than me. I didn't mean to start a huge comment thread, just wanted to point out first() for future readers :-)Goosander
B
0

As alasdair mentioned you could use the built in first() method. It returns the object if it exists or None if it's not

personalProfile = World.objects.filter(ID=personID).first()
Broaddus answered 3/5, 2022 at 12:44 Comment(0)
R
0

Try this it works perfect

World.objects.get_or_create(ID=personID)

Thanks to @Sayse

Renzo answered 13/2, 2024 at 0:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.