DoesNotExist behavior in db.models query.py

80 views
Skip to first unread message

st...@fitcode.com

unread,
Oct 23, 2017, 5:49:28 PM10/23/17
to Django users
I have been tripping over the following exception
DoesNotExist: INSERT_YOUR_MODEL_NAME_HERE matching query does not exist.

The error is source in django 1.11.2 in db.models query.py file, in the get(self, *args, **kwargs) function, line 378 to be exact
...
   
raise self.model.DoesNotExist(...)
...
This throws a 500 error and I would like my code not to throw a 500 error in this situation, rather a customized message more along a 4xx error e.g.
{"message": "record matching identifier A and identifier B not found in database"}
rather than a 500 error.

It seems to me that my only choice right now is to subclass QuerySet and write my own version of the get(self, *args, **kwargs) method. Is that my only choice or is there a django parameter I can set so that 0 matching results does not return a 500?

Or do I need to intercept the 500 error somewhere else.

Sorry, am a bit of a newbie to django internals, all help and advice is appreciated.

Thanks,
Steve

st...@fitcode.com

unread,
Oct 23, 2017, 6:14:41 PM10/23/17
to Django users
Replying to myself here: as is often the case, the problem lies with my own source code.
I was calling
MODELNAME.objects.get(modelfieldname1='foo',modelfieldname2='bar')


which raises the 500 when no such record exists.

Turns out that calling
MODELNAME.objects.filter(modelfieldname1='foo',modelfieldname2='bar')


behaves politely when finding no matching record and returns a 0 length QuerySet array. So with calling filter I can then just test on length of result and make my behavior omit a 400 and some helpful information to client side about the nature of the 400 error.

Separately, this does raise the question of should get() and filter() behave similarly or is there a reason that filter() allows empty result sets but get() does not?

Thanks,
Steve

Aaron C. de Bruyn

unread,
Oct 23, 2017, 7:06:41 PM10/23/17
to django...@googlegroups.com
The difference between .filter() and .get() is definitely 'by-design'.

When you filter through a list of objects, you could end up with zero,
one, or many objects returned.

When you call .get(), you are basically saying "I want to get exactly
*one* record". If the record is not found, it is considered an error.

One additional difference is that .filter() returns a list of zero or
more objects whereas .get() returns the object you requested or it
throws an error.

You can catch the error calling .get() like so:

try:
Person.objects.get(pk=123)
except Person.DoesNotExist:
print("That person did not exist")


-A
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to django-users...@googlegroups.com.
> To post to this group, send email to django...@googlegroups.com.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/67bd9382-0ac1-49c6-8b26-1827b91a852c%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.

st...@fitcode.com

unread,
Oct 23, 2017, 10:44:00 PM10/23/17
to Django users
Thank you Aaron, I started to figure this all out too as I read the docs more carefully and looked at the source code more thoughtfully.

One nuance on the try/except block you propose: doesn't work as expected in the request framework because even though the except block can be stated as you propose, the exception gets thrown by db.models query.py and is declared as a simple
raise DoesNotExist...

And this then bubbles all the way back through the middleware.

The simplest answer for my use case is to just use filter().

As get() is defined as a method of the QuerySet class, at first glance I assumed it would return a queryset, but as you point out, it doesn't. It returns an instance of a model class.  In my perfect world, get() would have been named as get_as_model_object so that it was clear this is a convenience method of the class. But that's just quibbling. Had I read the source code more carefully, it would have dawned on me what to do and why I had been confused.

Thank you.
steve


On Monday, October 23, 2017 at 2:49:28 PM UTC-7, st...@fitcode.com wrote:

James Schneider

unread,
Oct 23, 2017, 11:28:12 PM10/23/17
to django...@googlegroups.com


On Oct 23, 2017 4:06 PM, "'Aaron C. de Bruyn' via Django users" <django...@googlegroups.com> wrote:
The difference between .filter() and .get() is definitely 'by-design'.

When you filter through a list of objects, you could end up with zero,
one, or many objects returned.

When you call .get(), you are basically saying "I want to get exactly
*one* record".  If the record is not found, it is considered an error.

One additional difference is that .filter() returns a list of zero or
more objects whereas .get() returns the object you requested or it
throws an error.

A further distinction is that .filter() returns a single Queryset object, not a list of objects found via the query. It is only converted to a true list of objects when it is used (lazy), meaning that the query to the DB is delayed until the last possible moment. This allows for chaining of multiple .filter() calls or other ORM operations to be appended before the DB query is executed.

A .get() call returns the actual object, meaning that you can't chain other filters on to it.

A small distinction, but can land you in trouble if the database state changes between the creation of a Queryset object and when it is actually executed.

-James
Reply all
Reply to author
Forward
0 new messages