How do you catch this exception?

PythonDjangoException

Python Problem Overview


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.

Python Solutions


Solution 1 - Python

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 - Python

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 - Python

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:

  1. MainModel.related.RelatedObjectDoesNotExist
  2. RelatedModel.DoesNotExist
  3. 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 - Python

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 - Python

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

Solution 6 - Python

tdelaney's answer is great for regular code paths, but if you need to know how to catch this exception in tests:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionboatcoderView Question on Stackoverflow
Solution 1 - PythontdelaneyView Answer on Stackoverflow
Solution 2 - PythonFushView Answer on Stackoverflow
Solution 3 - PythonZagsView Answer on Stackoverflow
Solution 4 - PythonC SView Answer on Stackoverflow
Solution 5 - PythonMuhammad Faizan FareedView Answer on Stackoverflow
Solution 6 - PythonLisaDView Answer on Stackoverflow