How do you catch this exception?
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.
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:
To catch this exception in general, you can do
from django.core.exceptions import ObjectDoesNotExist
try:
# Your code here
except ObjectDoesNotExist:
# Handle exception