Comment attrapez-vous cette exception?

162

Ce code est dans django / db / models / fields.py Il crée / définit une exception?

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

C'est dans django / db / models / fields / related.py cela lève l'exception ci-dessus:

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

Le problème est que ce 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

n'attrapera pas cette exception

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

et

except related.RelatedObjectDoesNotExist:

Lève 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

c'est probablement pourquoi.

bateau
la source
Comment avez-vous importé related?
John Zwinck
4
utiliser à la AttributeErrorplace derelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine
Oui @JohnZwinck j'ai importé lié.
boatcoder

Réponses:

302

Si votre modèle associé s'appelle Foo, vous pouvez simplement faire:

except Foo.DoesNotExist:

Django est incroyable quand ce n'est pas terrifiant. RelatedObjectDoesNotExistest une propriété qui renvoie un type qui est déterminé dynamiquement à l'exécution. Ce type utilise self.field.rel.to.DoesNotExistcomme classe de base. Selon la documentation Django:

ObjectDoesNotExist et DoesNotExist

exception DoesNotExist

L' exception DoesNotExist est déclenchée lorsqu'un objet n'est pas trouvé pour les paramètres donnés d'une requête. Django fournit une exception DoesNotExist comme attribut de chaque classe de modèle pour identifier la classe d'objet qui n'a pas pu être trouvée et pour vous permettre d'attraper une classe de modèle particulière avec try/except .

C'est la magie qui fait que cela se produit. Une fois que le modèle a été construit, self.field.rel.to.DoesNotExistest l'exception n'existe pas pour ce modèle.

tdelaney
la source
7
Parce que le bogue était dans DjangoRestFramework, et le modèle est un peu difficile à trouver à ce stade. Je me suis contenté d'attraper ObjectDoesNotExist.
boatcoder
3
Vous pouvez également utiliser AttributeError, qui dans certaines circonstances peut être une meilleure option (cette erreur se produit presque toujours lorsque vous accédez à l '"attribut" d'un enregistrement, vous n'avez donc pas à savoir si cet attribut correspond à un record ou pas.
Jordan Reiter
1
Oui, d'accord. Mais pourquoi ne peut-il pas simplement renvoyer Aucun? Surtout avec les champs un-à-un. Ou y a-t-il une bonne raison?
Neil
61

Si vous ne souhaitez pas importer la classe de modèle associée, vous pouvez:

except MyModel.related_field.RelatedObjectDoesNotExist:

ou

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

related_fieldest le nom du champ.

Fush
la source
7
c'est en fait très utile au cas où vous auriez besoin d'éviter les importations circulaires. Merci
Giovanni Di Milia
40

Pour intercepter cette exception en général, vous pouvez faire

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception
Zags
la source
2
J'ai trouvé que cela n'attrapait pas l'erreur comme prévu. Le <Model>.DoesNotExistdid
Eric Blum
1
@EricBlum <Model> .DoesNotExist est un descendant d'ObjectDoesNotExist, donc cela ne devrait pas arriver. Pouvez-vous comprendre pourquoi cela se produit ou donner plus de détails sur votre code?
Zags
10

L' RelatedObjectDoesNotExistexception est créée dynamiquement lors de l'exécution. Voici l'extrait de code pertinent pour les descripteurs ForwardManyToOneDescriptoret 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),
        {}
    )

Ainsi, l'exception hérite de <model name>.DoesNotExistet AttributeError. En fait, le MRO complet pour ce type d'exception est:

[<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 chose à retenir est que vous pouvez capturer <model name>.DoesNotExist, ObjectDoesNotExist(importer depuis django.core.exceptions) ou AttributeErrortout ce qui a le plus de sens dans votre contexte.

CS
la source
3

Un peu tard mais utile pour les autres.

2 façons de gérer cela.

1er:

Quand nous devons attraper une exception

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

2ème: Quand je ne veux pas gérer l'exception

>>> hasattr(p2, 'restaurant')
False
Muhammad Faizan Fareed
la source
2

La réponse de tdelaney est idéale pour les chemins de code normaux, mais si vous avez besoin de savoir comment intercepter cette exception dans les tests:

from django.core.exceptions import ObjectDoesNotExist

...

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