Come si rileva questa eccezione?


162

Questo codice è in django / db / models / fields.py Crea / definisce un'eccezione?

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

Questo è in django / db / models / fields / related.py solleva la suddetta eccezione sopra:

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

Il problema è che questo codice:

    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

non prenderò quell'eccezione

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

e

except related.RelatedObjectDoesNotExist:

Alza 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

che è probabilmente il motivo.


Come hai importato related?
John Zwinck,

4
usa AttributeErrorinvece direlated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine il

Sì @JohnZwinck Ho importato correlati.
Boatcoder

Risposte:


302

Se il tuo modello correlato si chiama Foo, puoi semplicemente fare:

except Foo.DoesNotExist:

Django è incredibile quando non è terrificante. RelatedObjectDoesNotExistè una proprietà che restituisce un tipo che viene calcolato dinamicamente in fase di esecuzione. Quel tipo usa self.field.rel.to.DoesNotExistcome classe base. Secondo la documentazione di Django:

ObjectDoesNotExist e DoesNotExist

eccezione DoesNotExist

L' eccezione DoesNotExist viene sollevata quando non viene trovato un oggetto per i parametri dati di una query. Django fornisce un'eccezione DoesNotExist come attributo di ogni classe di modello per identificare la classe di oggetto che non è stato possibile trovare e per consentire di catturare una particolare classe di modello con try/except .

Questa è la magia che lo rende possibile. Una volta che il modello è stato creato, self.field.rel.to.DoesNotExistè l'eccezione inesistente per quel modello.


7
Perché il bug era in DjangoRestFramework, e il modello è piuttosto difficile da trovare a quel punto. Ho deciso di catturare ObjectDoesNotExist.
Boatcoder

3
Puoi anche utilizzare AttributeError, che in determinate circostanze potrebbe essere un'opzione migliore (questo errore si verifica quasi sempre quando si accede all '"attributo" di un record, quindi in questo modo non è necessario tenere traccia se questo attributo corrisponde a un record o no
Jordan Reiter,

1
Sì okay. Ma perché non può semplicemente restituire None? Soprattutto con i campi uno a uno. O c'è una buona ragione?
Neil,

61

Se non si desidera importare la relativa classe di modello, è possibile:

except MyModel.related_field.RelatedObjectDoesNotExist:

o

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

dov'è related_fieldil nome del campo.


7
questo è in realtà piuttosto utile nel caso in cui sia necessario evitare importazioni circolari. Grazie
Giovanni Di Milia il

40

Per cogliere questa eccezione in generale, puoi farlo

from django.core.exceptions import ObjectDoesNotExist

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

2
Ho scoperto che questo non ha effettivamente rilevato l'errore come previsto. Lo <Model>.DoesNotExistfece
Eric Blum,

1
@EricBlum <Model> .DoesNotExist è un discendente di ObjectDoesNotExist, quindi ciò non dovrebbe accadere. Puoi scoprire perché sta accadendo o fornire maggiori dettagli sul tuo codice?
Zags,

10

L' RelatedObjectDoesNotExisteccezione viene creata dinamicamente in fase di esecuzione. Ecco il frammento di codice rilevante per i descrittori ForwardManyToOneDescriptore 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),
        {}
    )

Quindi l'eccezione eredita da <model name>.DoesNotExiste AttributeError. In effetti, l'MRO completo per questo tipo di eccezione è:

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

L'asporto di base è che puoi catturare <model name>.DoesNotExist, ObjectDoesNotExist(importare da django.core.exceptions) o AttributeError, qualunque cosa abbia più senso nel tuo contesto.


2

La risposta di tdelaney è ottima per i percorsi di codice regolari, ma se devi sapere come intercettare questa eccezione nei test:

from django.core.exceptions import ObjectDoesNotExist

...

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

2

Un po 'in ritardo ma utile per gli altri.

2 modi per gestirlo.

1 °:

Quando dobbiamo prendere un'eccezione

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

2 °: quando non si desidera gestire l'eccezione

>>> hasattr(p2, 'restaurant')
False
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.