¿Cómo atrapas esta excepción?

162

Este código está en django / db / models / fields.py ¿Crea / define una excepción?

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

Esto está en django / db / models / fields / related.py plantea la excepción mencionada anteriormente:

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

El problema es que este código:

    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

no atrapará esa excepción

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

y

except related.RelatedObjectDoesNotExist:

Plantea un 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

probablemente por eso

codificador de barcos
fuente
¿Cómo has importado related?
John Zwinck
44
usar en AttributeErrorlugar derelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine
Sí @JohnZwinck he importado relacionados.
Boatcoder

Respuestas:

302

Si su modelo relacionado se llama Foo, simplemente puede hacer:

except Foo.DoesNotExist:

Django es increíble cuando no es aterrador. RelatedObjectDoesNotExistes una propiedad que devuelve un tipo que se calcula dinámicamente en tiempo de ejecución. Ese tipo se usa self.field.rel.to.DoesNotExistcomo una clase base. De acuerdo con la documentación de Django:

ObjectDoesNotExist y DoesNotExist

la excepción DoesNotExist

La excepción DoesNotExist se genera cuando no se encuentra un objeto para los parámetros dados de una consulta. Django proporciona una excepción DoesNotExist como un atributo de cada clase de modelo para identificar la clase de objeto que no se pudo encontrar y para permitirle capturar una clase de modelo particular con try/except .

Esta es la magia que hace que eso suceda. Una vez que el modelo ha sido construido, self.field.rel.to.DoesNotExistes la excepción de no existir para ese modelo.

Tdelaney
fuente
77
Debido a que el error estaba en DjangoRestFramework, y el modelo es algo difícil de encontrar en ese momento. Me decidí por atrapar ObjectDoesNotExist.
Boatcoder
3
También puede usar AttributeError, que bajo ciertas circunstancias puede ser una mejor opción (este error casi siempre ocurre cuando está accediendo al "atributo" de un registro, por lo que de esta manera no tiene que hacer un seguimiento si este atributo corresponde a un grabar o no.
Jordan Reiter
1
Sí, está bien. Pero, ¿por qué no puede devolver ninguno? Especialmente con campos uno a uno. ¿O hay una buena razón?
Neil
61

Si no desea importar la clase de modelo relacionada, puede:

except MyModel.related_field.RelatedObjectDoesNotExist:

o

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

donde related_fieldestá el nombre del campo

Fush
fuente
77
En realidad, esto es bastante útil en caso de que necesite evitar las importaciones circulares. Gracias
Giovanni Di Milia
40

Para atrapar esta excepción en general, puede hacer

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception
Zags
fuente
2
Encontré que esto realmente no captó el error como se esperaba. Lo <Model>.DoesNotExisthizo
Eric Blum
1
@EricBlum <Modelo> .DoesNotExist es un descendiente de ObjectDoesNotExist, por lo que eso no debería suceder. ¿Puedes investigar por qué está sucediendo o dar más detalles sobre tu código?
Zags
10

La RelatedObjectDoesNotExistexcepción se crea dinámicamente en tiempo de ejecución. Aquí está el fragmento de código relevante para los descriptores ForwardManyToOneDescriptory 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),
        {}
    )

Entonces la excepción hereda de <model name>.DoesNotExisty AttributeError. De hecho, el MRO completo para este tipo de excepción es:

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

La conclusión básica es que puedes atrapar <model name>.DoesNotExist, ObjectDoesNotExist(importar desde django.core.exceptions) o AttributeError, lo que tenga más sentido en tu contexto.

CS
fuente
2

La respuesta de tdelaney es excelente para rutas de código normales, pero si necesita saber cómo detectar esta excepción en las pruebas:

from django.core.exceptions import ObjectDoesNotExist

...

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

Un poco tarde pero útil para los demás.

2 formas de manejar esto.

1er:

Cuando necesitamos atrapar excepciones

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

2do: Cuando no quiere manejar una excepción

>>> hasattr(p2, 'restaurant')
False
Muhammad Faizan Fareed
fuente