Як ви спіймаєте цей виняток?


162

Цей код є у django / db / models /elds.py Він створює / визначає виняток?

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),
            {}
        )

Це в django / db / models / field / related.py, це збільшує зазначене виняток вище:

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

Проблема полягає в тому, що цей код:

    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

не вийде цього винятку

>>>print type(foo)
<class 'django.db.models.fields.related.RelatedObjectDoesNotExist'>
>>>isinstance(foo, related.FieldDoesNotExist)
False

і

except related.RelatedObjectDoesNotExist:

Піднімає 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

що, мабуть, саме тому.


Як ви імпортували related?
Джон Цвінк

4
використовувати AttributeErrorзамістьrelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine

Так @JohnZwinck Я імпортував пов'язані.
boatcoder

Відповіді:


302

Якщо ваша пов'язана модель називається Foo, ви можете просто зробити:

except Foo.DoesNotExist:

Джанго дивовижно, коли це не страшно. RelatedObjectDoesNotExist- властивість, яка повертає тип, який динамічно з'ясовується під час виконання. Цей тип використовується self.field.rel.to.DoesNotExistяк базовий клас. Відповідно до документації Django:

ObjectDoesNotExist і DoesNotExist

виняток DoesNotExist

DoesNotExist збуджується виключення , коли об'єкт не знайдено для заданих параметрів запиту. Django надає виняток DoesNotExist як атрибут кожного модельного класу, щоб ідентифікувати клас об'єкта, який не вдалося знайти, і щоб ви могли спіймати певний клас моделі за допомогою try/except .

Це магія, завдяки якій це відбувається. Після того, як модель була побудована, self.field.rel.to.DoesNotExistце не існує виключення для цієї моделі.


7
Тому що помилка була в DjangoRestFramework, і модель дещо важко подолати в цей момент. Я погодився ловити ObjectDoesNotExist.
boatcoder

3
Ви також можете скористатися AttributeError, який за певних обставин може бути кращим варіантом (ця помилка майже завжди виникає, коли ви отримуєте доступ до "атрибуту" запису, тому таким чином вам не доведеться відслідковувати, чи відповідає цей атрибут a запис чи ні.
Jordan Reiter

1
Так, добре. Але чому він не може просто повернути None? Особливо з полями один на один. Або є вагомі причини?
Ніл

61

Якщо ви не хочете імпортувати відповідний клас моделі, ви можете:

except MyModel.related_field.RelatedObjectDoesNotExist:

або

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

де related_fieldназва поля.


7
це насправді дуже корисно, якщо вам потрібно уникати кругового імпорту. Спасибі
Джованні Ді Мілія

40

Щоб зловити цей виняток загалом, ви можете зробити

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception

2
Я виявив, що це насправді не виявило помилку, як очікувалося. <Model>.DoesNotExistЗробив
Eric Блюм

1
@EricBlum <Model> .DoesNotExist є нащадком ObjectDoesNotExist, тому цього не повинно відбуватися. Чи можете ви розібратися, чому це відбувається, або дати детальну інформацію про свій код?
Загс

10

RelatedObjectDoesNotExistВиняток створюється динамічно під час виконання. Ось відповідний фрагмент коду для ForwardManyToOneDescriptorта ReverseOneToOneDescriptorдескрипторів:

@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),
        {}
    )

Тож виняток успадковується від <model name>.DoesNotExistі AttributeError. Насправді повна МРО для цього виду винятків:

[<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'>]

Основний винос - ви можете зловити <model name>.DoesNotExist , ObjectDoesNotExist(імпортувати з django.core.exceptions) або AttributeError, що має найбільш сенс у вашому контексті.


2

Відповідь tdelaney чудово підходить для звичайних кодових шляхів, але якщо вам потрібно знати, як зловити цей виняток у тестах:

from django.core.exceptions import ObjectDoesNotExist

...

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

2

Трохи пізно, але корисно для інших.

2 способи впоратися з цим.

1-й:

Коли нам потрібно зловити виняток

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

2-й: Коли не хочеться обробляти винятки

>>> hasattr(p2, 'restaurant')
False
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.