Wie fangen Sie diese Ausnahme?


161

Dieser Code befindet sich in django / db / models / fields.py. Erstellt / definiert eine Ausnahme?

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

Dies ist in django / db / models / fields / related.py die oben genannte Ausnahme:

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

Das Problem ist, dass dieser 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

wird diese Ausnahme nicht fangen

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

und

except related.RelatedObjectDoesNotExist:

Erhöht eine 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

Das ist wahrscheinlich der Grund.


Wie haben Sie importiert related?
John Zwinck

4
Verwenden Sie AttributeErroranstelle vonrelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
Catherine

Ja @JohnZwinck Ich habe verwandte importiert.
Bootscodierer

Antworten:


301

Wenn Ihr verwandtes Modell Foo heißt, können Sie einfach Folgendes tun:

except Foo.DoesNotExist:

Django ist erstaunlich, wenn es nicht erschreckend ist. RelatedObjectDoesNotExistist eine Eigenschaft, die einen Typ zurückgibt, der zur Laufzeit dynamisch ermittelt wird. Dieser Typ wird self.field.rel.to.DoesNotExistals Basisklasse verwendet. Laut Django-Dokumentation:

ObjectDoesNotExist und DoesNotExist

Ausnahme DoesNotExist

Die DoesNotExist- Ausnahme wird ausgelöst , wenn für die angegebenen Parameter einer Abfrage kein Objekt gefunden wird. Django stellt eine DoesNotExist- Ausnahme als Attribut jeder Modellklasse bereit , um die nicht gefundene Objektklasse zu identifizieren und um eine bestimmte Modellklasse mit try/ exceptabzufangen .

Dies ist die Magie, die das möglich macht. Sobald das Modell erstellt wurde, self.field.rel.to.DoesNotExistist dies die nicht existierende Ausnahme für dieses Modell.


7
Weil der Fehler in DjangoRestFramework war und das Modell zu diesem Zeitpunkt etwas schwer zu finden ist. Ich habe mich entschlossen, ObjectDoesNotExist zu fangen.
Bootscodierer

3
Sie können auch AttributeError verwenden, was unter bestimmten Umständen eine bessere Option sein kann (dieser Fehler tritt fast immer auf, wenn Sie auf das "Attribut" eines Datensatzes zugreifen, sodass Sie auf diese Weise nicht verfolgen müssen, ob dieses Attribut einem entspricht Rekord oder nicht.
Jordan Reiter

1
Yeaaaa okay. Aber warum kann es nicht einfach None zurückgeben? Besonders bei Eins-zu-Eins-Feldern. Oder gibt es einen guten Grund?
Neil

61

Wenn Sie die zugehörige Modellklasse nicht importieren möchten, können Sie:

except MyModel.related_field.RelatedObjectDoesNotExist:

oder

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

Wo related_fieldist der Feldname?


7
Dies ist eigentlich sehr nützlich, wenn Sie zirkuläre Importe vermeiden müssen. Vielen Dank
Giovanni Di Milia

40

Um diese Ausnahme im Allgemeinen abzufangen, können Sie dies tun

from django.core.exceptions import ObjectDoesNotExist

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

2
Ich fand, dass dies den Fehler nicht wie erwartet abfing. Das <Model>.DoesNotExisttat
Eric Blum

1
@EricBlum <Modell> .DoesNotExist ist ein Nachkomme von ObjectDoesNotExist, daher sollte dies nicht passieren. Können Sie herausfinden, warum dies geschieht, oder weitere Details zu Ihrem Code angeben?
Zags

10

Die RelatedObjectDoesNotExistAusnahme wird zur Laufzeit dynamisch erstellt. Hier ist das relevante Code-Snippet für die ForwardManyToOneDescriptorund ReverseOneToOneDescriptorDeskriptoren:

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

Die Ausnahme erbt also von <model name>.DoesNotExistund AttributeError. Tatsächlich lautet die vollständige MRO für diesen Ausnahmetyp:

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

Der Grundservice ist man fangen kann <model name>.DoesNotExist, ObjectDoesNotExist(Import aus django.core.exceptions) oder AttributeError, was auch immer die am meisten Sinn in Ihrem Kontext macht.


2

Die Antwort von tdelaney eignet sich hervorragend für reguläre Codepfade. Wenn Sie jedoch wissen müssen, wie Sie diese Ausnahme in Tests abfangen können:

from django.core.exceptions import ObjectDoesNotExist

...

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

2

Etwas spät, aber hilfreich für andere.

2 Möglichkeiten, damit umzugehen.

1 :

Wenn wir eine Ausnahme machen müssen

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

2. Wenn Sie keine Ausnahme behandeln möchten

>>> hasattr(p2, 'restaurant')
False
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.