Django exists() versus DoesNotExist - python

I have some questions about django exists() and DoesNotExist exception.
Example code:
id = 1
# first
if User.objects.get(pk=id).exists():
# my logic
pass
# second
try:
User.objects.get(pk=id)
# my logic
pass
except User.DoesNotExist:
return 0
I often use get() method. Which practice is better? Which code is better? The first or second?

if User.objects.get(pk=id).exists()
This won't work, so the question is pretty easy to answer: This way is inferior to the ways which do work :-)
I guess you actually didn't make a Minimal Complete Verifiable Example and so missed the error when you posted un-verified code.
So instead, I suppose you are asking about the difference between:
QuerySet.exists() when you have a QuerySet (e.g. from a filter operation).
For example:
if User.objects.filter(pk=id).exists():
# ... do the things that need that user to exist
Model.objects.get(…) and catching the Model.DoesNotExist exception type (or, if you want to be more general, the parent type ObjectDoesNotExist).
For example:
try:
user = User.objects.get(pk=id)
except User.DoesNotExist:
# ... handle the case of that user not existing
The difference is:
The QuerySet.exists method is on a queryset, meaning you ask it about a query (“are there any instances matching this query?”), and you're not yet attempting to retrieve any specific instance.
The DoesNotExist exception for a model is raised when you actually attempted to retrieve one instance, and it didn't exist.
Use whichever one correctly expresses your intention.

You can find more info in docs:
about exists(),but exists() works only for QuerySet
Returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible, but it does execute nearly the same query as a normal QuerySet query.
exists() is useful for searches relating to both object membership in a QuerySet and to the existence of any objects in a QuerySet, particularly in the context of a large QuerySet.
But ObjectDoesNotExist works only with get().
Also you can try another approach:
user = User.objects.filter(id=2)
if user:
# put your logic
pass

Since we are in Django, we'll try to catch the error with Django functionality instead of the common way(which is using Exceptions with Python).
id = 1
def a_query(id):
qs = User.objects.filter(pk=id)
if qs.exists():
return qs.first()
return None
In here, the method exists() helps you catching the error(if there's any).
ref: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.exists

in django model,
if you gonna use model.objects.get() if it wasn't exist it raise an error. in that case you can use DoesNotExist along with except:
try:
val = Model.objects.get(pk=val) # if nothing found it will raise an exception
exception:
you can trace an exception without mentioning anything on top.
(or)
exception ObjectDoesNotExist:
# it will come here if exception is DoesNotExist

For Django version 2.0.6, you can do the following, and it will work:
if Model.objects.filter(my_id=objectid).exists():
myobject = get_object_or_404(Model, my_id=objectid)
context = {'myobject': myobject}
return render(request, self.template_name, context)
You can get more info here: https://docs.djangoproject.com/en/2.1/ref/models/querysets/

It's my understanding that you're asking whether to use if statements or try catch on your code. I personally prefer to avoid using try catch, a think it's an ugly syntax, when I do want to raise an exception, I use a python keyword raise, to me, it makes the code cleaner.
Code example:
user = User.objects.filter(id=2)
if not user:
raise ObjectDoesNotExist

Related

Django objects.get() function does not work but objects.filter().first() works

When I look up for the user with
User.objects.get(username=username)
it shows error
User matching query does not exist
but it works fine with User.objects.filter(username=username).first()
why does this happen?
The two are not equivalent. .get(…) [Django-doc] will raise an exception in two cases:
when there is no User with the given username; and
when there are multiple Users with the given username, although here that is impossible, since the username is a unique=True field.
.first(…) [Django-doc] on the other hand will not raise an exception. In case no record is returned, it will return None. In case there are multiple records that match, it will simply return the first one.
It is however often not a good idea to pass errors silently. In case you expect a user, using .first() will result in None, but this can mean that the logic later in the view that expects a User object will either raise errors, or make mistakes. In a view often get_object_or_404 [Django-doc] is used to raise a Http404 in case no object exists, this thus means that the view will respond with a HTTP 404 response.

Better way to check if a object is present in Django ORM among Get/Filter?

Lets consider I am trying to find if a user with primary key 20 exists or not? I can do this in 2 ways.
The First one :
try:
user = User.objects.get(pk=20)
except User.DoesNotExist:
handle_non_existent_user()
The other way could be :
users = User.objects.filter(pk=20)
if not users.exists():
handle_non_existent_user()
Which is better method to do check existence?
This might be related to this : What is the best way to check if data is present in django?
However, people favoured the first method because of specified examples did not had the reference of model queryset.
Also in the answer of following question : what is the right way to validate if an object exists in a django view without returning 404?
It is largely based because we are not getting the reference of object in question.
TLDR: For cases when you almost always sure that the object is db it is better to use try:get for cases when there is 50% chance that object doesn't exists then it is better to use if:filter.exists
It really depends on code context. For example there are cases when if statement is better than try/except Using try vs if in python
So for your question it is the same Difference between Django's filter() and get() methods. get method underneath calls filter
https://github.com/django/django/blob/stable/1.11.x/django/db/models/query.py#L366
def get(self, *args, **kwargs):
"""
Performs the query and returns a single object matching the given
keyword arguments.
"""
clone = self.filter(*args, **kwargs)
if self.query.can_filter() and not self.query.distinct_fields:
clone = clone.order_by()
num = len(clone)
if num == 1:
return clone._result_cache[0]
if not num:
raise self.model.DoesNotExist(
"%s matching query does not exist." %
self.model._meta.object_name
)
raise self.model.MultipleObjectsReturned(
"get() returned more than one %s -- it returned %s!" %
(self.model._meta.object_name, num)
)
So in case when you use filter with exists. It will do almost the same code because exists underneath does this
def exists(self):
if self._result_cache is None:
return self.query.has_results(using=self.db)
return bool(self._result_cache)
And as you can see filter.exists will execute less code and should work faster, but it doesn't return you an object.
The first one is the best way in my opinion because if I ever forget to check whether the user exists or not, it will give me an error even if I don't use the try / except clause.
Also, get was made specifically to get ONE item only.

What's the most pythonic way to get name of the field when an exception related to IntegrityError is thrown?

I'm developing an API with DRF and I want to generate and return proper custom error messages when an exception related to IntegrityError is thrown.
To do this, I've implemented a custom exception handler. Inside the custom exception handler, I want to get the name of the field that causes the error from the Exception instance and then I'll generate and return proper message in the response.
Currently, I can do this by parsing the message attribute of the Exception instance but I'm not sure this is the best possible solution.
So, is there any pythonic way to get the name of the field from the Exception instance when an exception related to IntegrityError is thrown?
Thanks!
According to source it seems IntergityError is no more than an Exception so the only way is to parsing exception.message or exception.args.
You always can check what gives you print(dir(exception)) but I'm pretty sure only message and args will be helpful.
IMHO "parsing the message" is perfectly pythonic: simple, readable, you can do the same/similar solution in any other similar situation. I've seen something like this before:
# in a custom Serializer's create() method
try:
return super().create(validated_data)
except IntegrityError as e:
raise APIException(detail=e.args[0].split('DETAIL: ')[1])
It simple, it works and getting the extra data in any other way will likely involve something significantly more complicated.

What is the best way to get an object which is not sure if there is or not in Django model

What is the best way to get an object which is not sure if there is or not.(in Django model)
I think there are three options.
One is to use try and except statement to process the exception.
Second is to use objects.filter() instead of objects.get() and check query_set.count().
Third is to use objects.count() first and then use objects.get() if there is.
You can use .first() operation on a queryset to your advantage to get the first object of the queryset if it exists and None otherwise.
Returns the first object matched by the queryset, or None if there is
no matching object.
my_object = some_queryset.first() # return a object or 'None'
This will return the first object of the queryset if there are objects in the queryset. Otherwise, it returns None. It will automatically handle the case when objects do no exist without you needing to write try-except.
The above code is equivalent to writing:
try:
my_object = some_queryset[0] # access the first element
except IndexError:
my_object = None
Note: Here, we needed the object, so we are using .first(). If we needed to check only if the object exists, then we could have used .exists()
Edited: I think the best solution in this case, is use this third package
django-annoying
This packaged contains a lot of syntactic sugar for things like that.
With this package, you can use the function get_object_or_None, similar to
get_object_or_404.
But, the real code behind this function is :
def get_object_or_None(klass, *args, **kwargs):
"""
Uses get() to return an object or None if the 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.
Note: Like with get(), a MultipleObjectsReturned will be raised if more than one
object is found.
"""
queryset = _get_queryset(klass)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
return None
Note: Don’t use this if all you want to do is determine if at least one result exists. It’s more efficient to use exists().

Proper use of exceptions in python/django orm

I'm trying to get an object, if it existed ok if not then something else and so on. Is it correct to do the following? I've heared that exceptions are expensive and shuold be avoided, is that correct?
try:
user = User.objects.get(user=id)
except ObjectDoesNotExist:
try:
user = User.objects.get(email=id)
except ObjectDoesNotExist:
try:
# ...
finally:
# do the final thing
They are somewhat expensive, but certainly not too expensive to use when needed. I'd be more concerned about hammering the database with multiple queries You can avoid both problems by getting the results for all possible fields back in one query.
from django.contrib.auth.models import User
from django.db.models import Q
user = User.objects.filter(Q(user=id) | Q(email=id) | [...])[0]
This relies on django's Q-objects, which allow you to create conditions joined together in more complex ways than the usual AND joins that you usually get when building filters. If you aren't concerned about the possibility of getting multiple objects back, you can still use the get() method like you did in your example.
The cost of a try/except is explained here: cost-of-exception-handlers-in-python
I suggest to catch things that should not happen or only happen rarely (real exceptions) with a try/except and more common situations with conditions.
Especially in a case like a Model.objects.get() where the underlying sql returns an empty list that wouldn't raise an exception if called as a filter.
users = User.objects.filter(user=id)[:1]
user = users and users[0]

Categories