Jak łapiesz ten wyjątek?

162

Ten kod znajduje się w django / db / models / fields.py Czy tworzy / definiuje wyjątek?

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

To jest w django / db / models / fields / related.py powoduje to powyższy wyjątek:

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

Problem w tym, że ten kod:

    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

nie złapie tego wyjątku

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

i

except related.RelatedObjectDoesNotExist:

Podnosi plik 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

i prawdopodobnie dlatego.

boatcoder
źródło
Jak zaimportowałeś related?
John Zwinck,
4
użyj AttributeErrorzamiastrelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine
Tak @JohnZwinck Zaimportowałem powiązane.
boatcoder

Odpowiedzi:

302

Jeśli Twój powiązany model nazywa się Foo, możesz po prostu:

except Foo.DoesNotExist:

Django jest niesamowite, gdy nie jest przerażające. RelatedObjectDoesNotExistto właściwość, która zwraca typ, który jest obliczany dynamicznie w czasie wykonywania. Ten typ używa self.field.rel.to.DoesNotExistjako klasy bazowej. Według dokumentacji Django:

ObjectDoesNotExist i DoesNotExist

wyjątek DoesNotExist

DoesNotExist jest wyjątek, kiedy obiekt nie znajduje się w podanych parametrach zapytania. Django udostępnia wyjątek DoesNotExist jako atrybut każdej klasy modelu w celu zidentyfikowania klasy obiektu, którego nie można było znaleźć i aby umożliwić przechwycenie określonej klasy modelu za pomocą try/except .

To jest magia, która to umożliwia. Po utworzeniu modelu self.field.rel.to.DoesNotExistjest to nieistniejący wyjątek dla tego modelu.

tdelaney
źródło
7
Ponieważ błąd był w DjangoRestFramework, a model jest nieco trudny do zdobycia w tym momencie. Zdecydowałem się złapać ObjectDoesNotExist.
boatcoder
3
Możesz także użyć AttributeError, który w pewnych okolicznościach może być lepszą opcją (ten błąd prawie zawsze występuje podczas uzyskiwania dostępu do „atrybutu” rekordu, więc w ten sposób nie musisz śledzić, czy ten atrybut odpowiada rekord czy nie
Jordan Reiter
1
Tak, w porządku. Ale dlaczego nie może po prostu zwrócić Brak? Zwłaszcza w przypadku pól jeden do jednego. Czy jest dobry powód?
Neil
61

Jeśli nie chcesz importować powiązanej klasy modelu, możesz:

except MyModel.related_field.RelatedObjectDoesNotExist:

lub

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

gdzie related_fieldjest nazwa pola.

Fush
źródło
7
jest to całkiem przydatne, jeśli chcesz uniknąć importu cyklicznego. Dzięki
Giovanni Di Milia
40

Aby ogólnie złapać ten wyjątek, możesz to zrobić

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception
Zags
źródło
2
Okazało się, że to nie wykryło błędu zgodnie z oczekiwaniami. <Model>.DoesNotExistZrobił
Eric Blum
1
@EricBlum <Model> .DoesNotExist jest potomkiem ObjectDoesNotExist, więc to nie powinno się zdarzyć. Czy możesz dowiedzieć się, dlaczego tak się dzieje lub podać więcej szczegółów na temat swojego kodu?
Zags
10

RelatedObjectDoesNotExistWyjątkiem jest tworzony dynamicznie w czasie pracy. Oto odpowiedni fragment kodu dla deskryptorów ForwardManyToOneDescriptori 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),
        {}
    )

Więc wyjątek dziedziczy po <model name>.DoesNotExisti AttributeError. W rzeczywistości pełna MRO dla tego typu wyjątku to:

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

Podstawowym wynos to można złapać <model name>.DoesNotExist, ObjectDoesNotExist(import z django.core.exceptions) lub AttributeError, co sprawia, że największy sens w swoim kontekście.

CS
źródło
2

Odpowiedź tdelaney jest świetna w przypadku zwykłych ścieżek kodu, ale jeśli chcesz wiedzieć, jak złapać ten wyjątek w testach:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()
LisaD
źródło
2

Trochę późno, ale pomocne dla innych.

2 sposoby radzenia sobie z tym.

1:

Kiedy musimy złapać wyjątek

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

2nd: Kiedy nie chcesz obsługiwać wyjątku

>>> hasattr(p2, 'restaurant')
False
Muhammad Faizan Fareed
źródło