How do you catch this exception?
Solution 1
If your related model is called Foo you can just do:
except Foo.DoesNotExist:
Django is amazing when it's not terrifying. RelatedObjectDoesNotExist
is a property that returns a type that is figured out dynamically at runtime. That type uses self.field.rel.to.DoesNotExist
as a base class.
According to Django documentation:
DoesNotExist
exception Model.DoesNotExist
This exception is raised by the ORM when an expected object is not found. For example,
QuerySet.get()
will raise it when no object is found for the given lookups.Django provides a
DoesNotExist
exception as an attribute of each model class to identify the class of object that could not be found, allowing you to catch exceptions for a particular model class.The exception is a subclass of
django.core.exceptions.ObjectDoesNotExist
.
This is the magic that makes that happen. Once the model has been built up, self.field.rel.to.DoesNotExist
is the does-not-exist exception for that model.
Solution 2
If you don't want to import the related model class, you can:
except MyModel.related_field.RelatedObjectDoesNotExist:
or
except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:
where related_field
is the field name.
Solution 3
Let's say we have the following models:
class MainModel(Model):
pass
class RelatedModel(Model):
main = OneToOneField(MainModel, null=True, related_name="related")
You can get a RelatedObjectDoesNotExist
exception with MainModel().related
.
You have three options for catching this exception, which you can find by looking at .__class__.__mro__
of the exception:
MainModel.related.RelatedObjectDoesNotExist
RelatedModel.DoesNotExist
django.core.exceptions.ObjectDoesNotExist
MainModel.related.RelatedObjectDoesNotExist
RelatedObjectDoesNotExist
is what the question is looking for, but is specific to a nullable OneToOneField
:
try:
# Your code here
except MainModel.related.RelatedObjectDoesNotExist:
# Handle exception
RelatedModel.DoesNotExist
Model.DoesNotExist
is the parent class of RelatedObjectDoesNotExist
. To catch it requires you to be able to import the model in question, but is a more generically useful code pattern.
try:
# Your code here
except OtherModel.DoesNotExist:
# Handle exception
django.core.exceptions.ObjectDoesNotExist
ObjectDoesNotExist
is the parent class of Model.DoesNotExist
. This will catch this exception for any model, which is helpful if you don't know what model will raise the exception:
from django.core.exceptions import ObjectDoesNotExist
try:
# Your code here
except ObjectDoesNotExist:
# Handle exception
Solution 4
The RelatedObjectDoesNotExist
exception is created dynamically at runtime. Here is the relevant code snippet for the ForwardManyToOneDescriptor
and ReverseOneToOneDescriptor
descriptors:
@cached_property
def RelatedObjectDoesNotExist(self):
# The exception can't be created at initialization time since the
# related model might not be resolved yet; `self.field.model` might
# still be a string model reference.
return type(
'RelatedObjectDoesNotExist',
(self.field.remote_field.model.DoesNotExist, AttributeError),
{}
)
So the exception inherits from <model name>.DoesNotExist
and AttributeError
. In fact, the complete MRO for this exception type is:
[<class 'django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist'>,
<class '<model module path>.DoesNotExist'>,
<class 'django.core.exceptions.ObjectDoesNotExist'>,
<class 'AttributeError'>,
<class 'Exception'>,
<class 'BaseException'>,
<class 'object'>]
The basic takeaway is you can catch <model name>.DoesNotExist
, ObjectDoesNotExist
(import from django.core.exceptions
) or AttributeError
, whatever makes the most sense in your context.
Solution 5
Little bit late but helpful for others.
2 ways to handle this.
1st :
When we need to catch exception
>>> from django.core.exceptions import ObjectDoesNotExist >>> try: >>> p2.restaurant >>> except ObjectDoesNotExist: >>> print("There is no restaurant here.") There is no restaurant here.
2nd: When don't want to handle exception
>>> hasattr(p2, 'restaurant') False
Related videos on Youtube
boatcoder
Started writing code in 1982 (in college) and haven't stopped since. I've used C++ to build applications for Libraries (the kind that lend books) and Banks and sleep labs. I've also written Java for banks. Worked with Pyramid in Grand Cayman for Uniregistry. Been working with Django since 2009 and have used it to build solutions for banks, sleep labs, and ALS clinics. Built a labor scheduling tool for the entertainment industry that uses AngularJS for the frontend SPA and Django rest framework for the back end API and DJango views for reports. I've been the rescue engineer for 911 calls that were getting lost, SQL queries that were so slow the application was timing them out, and written various scripts to patch around otherwise broken software packages where the source code was not available. A long time ago, in a galaxy far far away I wrote kernel code for Unix SYSV in the ethernet/FDDI arena for Intergraph.
Updated on November 07, 2022Comments
-
boatcoder over 1 year
This code is in django/db/models/fields.py It creates/defines an exception?
class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescriptorMethods)): # This class provides the functionality that makes the related-object # managers available as attributes on a model class, for fields that have # a single "remote" value, on the class that defines the related field. # In the example "choice.poll", the poll attribute is a # ReverseSingleRelatedObjectDescriptor instance. def __init__(self, field_with_rel): self.field = field_with_rel self.cache_name = self.field.get_cache_name() @cached_property def RelatedObjectDoesNotExist(self): # The exception can't be created at initialization time since the # related model might not be resolved yet; `rel.to` might still be # a string model reference. return type( str('RelatedObjectDoesNotExist'), (self.field.rel.to.DoesNotExist, AttributeError), {} )
This is in django/db/models/fields/related.py it raises the said exception above:
def __get__(self, instance, instance_type=None): if instance is None: return self try: rel_obj = getattr(instance, self.cache_name) except AttributeError: val = self.field.get_local_related_value(instance) if None in val: rel_obj = None else: params = dict( (rh_field.attname, getattr(instance, lh_field.attname)) for lh_field, rh_field in self.field.related_fields) qs = self.get_queryset(instance=instance) extra_filter = self.field.get_extra_descriptor_filter(instance) if isinstance(extra_filter, dict): params.update(extra_filter) qs = qs.filter(**params) else: qs = qs.filter(extra_filter, **params) # Assuming the database enforces foreign keys, this won't fail. rel_obj = qs.get() if not self.field.rel.multiple: setattr(rel_obj, self.field.related.get_cache_name(), instance) setattr(instance, self.cache_name, rel_obj) if rel_obj is None and not self.field.null: raise self.RelatedObjectDoesNotExist( "%s has no %s." % (self.field.model.__name__, self.field.name) ) else: return rel_obj
The problem is that this code:
try: val = getattr(obj, attr_name) except related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist: val = None # Does not catch the thrown exception except Exception as foo: print type(foo) # Catches here, not above
won't catch that exception
>>>print type(foo) <class 'django.db.models.fields.related.RelatedObjectDoesNotExist'> >>>isinstance(foo, related.FieldDoesNotExist) False
and
except related.RelatedObjectDoesNotExist:
Raises an
AttributeError: 'module' object has no attribute 'RelatedObjectDoesNotExist'
>>>isinstance(foo, related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist) Traceback (most recent call last): File "<string>", line 1, in <fragment> TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
which is probably why.
-
John Zwinck over 9 yearsHow have you imported
related
? -
catherine over 9 yearsuse
AttributeError
instead ofrelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
-
boatcoder over 9 yearsYes @JohnZwinck I have imported related.
-
-
boatcoder over 9 yearsBecause the bug was in DjangoRestFramework, and the model is somewhat hard to come by at that point. I settled for catching ObjectDoesNotExist.
-
Giovanni Di Milia over 7 yearsthis is actually pretty useful in case you need to avoid circular imports. Thanks
-
Jordan Reiter about 6 yearsYou can also use AttributeError, which under certain circumstances may be a better option (this error almost always occurs when you are accessing the "attribute" of a record, so this way you don't have to keep track whether this attribute corresponds to a record or not.
-
Eric Blum about 6 yearsI found this didn't actually catch the error as expected. The
<Model>.DoesNotExist
did -
Zags about 6 years@EricBlum <Model>.DoesNotExist is a descendant of ObjectDoesNotExist, so that shouldn't happen. Can you dig into why it is happening or give more details about your code?
-
Neil almost 5 yearsYeaaaa okay. But why can't it just return None? Especially with one-to-one fields. Or is there a good reason?
-
Michael P over 2 yearsThis is all because of how the
hasattr
logic was changed in python 3. In order to work withhasattr
it must be an exception that derives from the AttributeError class. Yet it must be also an exception that comes from the Model.DoesNotExist class. -
Osama Abuhamdan over 2 yearshasattr() is the right way to do it I think