你如何捕捉到这个异常?

Posted

技术标签:

【中文标题】你如何捕捉到这个异常?【英文标题】:How do you catch this exception? 【发布时间】:2014-12-03 21:20:22 【问题描述】:

这段代码在 django/db/models/fields.py 它创建/定义了一个异常?

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

这在 django/db/models/fields/related.py 中它引发了上述异常:

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

问题在于这段代码:

    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

不会捕获该异常

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

except related.RelatedObjectDoesNotExist:

引发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

这可能就是原因。

【问题讨论】:

你是如何导入related的? 使用AttributeError 而不是related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist 是的@JohnZwinck 我已经导入了相关的。 【参考方案1】:

如果您的相关模型称为 Foo,您可以这样做:

except Foo.DoesNotExist:

当 Django 不可怕时,它是惊人的。 RelatedObjectDoesNotExist 是一个属性,它返回在运行时动态计算出的类型。该类型使用self.field.rel.to.DoesNotExist 作为基类。

根据 Django 文档:

DoesNotExist

异常 Model.DoesNotExist

当预期对象不存在时,ORM 会引发此异常 成立。例如 QuerySet.get() 将在没有对象时将其提升 为给定的查找找到。

Django 提供了一个 DoesNotExist 异常作为 每个模型类来识别不能被识别的对象的类 找到,允许您捕获特定模型类的异常。

例外是 django.core.exceptions.ObjectDoesNotExist 的子类。

这就是实现这一目标的魔力。建立模型后,self.field.rel.to.DoesNotExist 是该模型的不存在异常。

【讨论】:

因为这个 bug 是在 DjangoRestFramework 中,而且那个时候模型有点难找到。我决定抓住 ObjectDoesNotExist。 你也可以使用AttributeError,在某些情况下这可能是一个更好的选择(这个错误几乎总是在你访问记录的“属性”时发生,所以这样你就不必跟踪此属性是否对应于记录。 是啊,好吧。但是为什么它不能只返回 None 呢?尤其是一对一的字段。还是有充分的理由? 这完全是因为hasattr 逻辑在 python 3 中的变化。为了使用hasattr,它必须是从 AttributeError 类派生的异常。然而,它也必须是来自 Model.DoesNotExist 类的异常。【参考方案2】:

有点晚了,但对其他人有帮助。

2 种方法来处理这个问题。

第一:

当我们需要捕获异常时

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

第二次: 当不想处理异常时

>>> hasattr(p2, 'restaurant')
False

【讨论】:

【参考方案3】:

RelatedObjectDoesNotExist 异常是在运行时动态创建的。这是ForwardManyToOneDescriptorReverseOneToOneDescriptor描述符的相关代码sn-p:

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

所以异常继承自&lt;model name&gt;.DoesNotExistAttributeError。事实上,这种异常类型的完整 MRO 是:

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

基本要点是您可以捕获&lt;model name&gt;.DoesNotExistObjectDoesNotExist(从django.core.exceptions 导入)或AttributeError,只要在您的上下文中最有意义。

【讨论】:

【参考方案4】:

tdelaney 的回答非常适合常规代码路径,但如果您需要知道如何在测试中捕获此异常:

from django.core.exceptions import ObjectDoesNotExist

...

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

【讨论】:

【参考方案5】:

一般来说,要捕获此异常,您可以这样做

from django.core.exceptions import ObjectDoesNotExist

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

【讨论】:

我发现这实际上并没有按预期捕获错误。 &lt;Model&gt;.DoesNotExist 做到了 @EricBlum .DoesNotExist 是 ObjectDoesNotExist 的后代,所以这不应该发生。您能否深入了解它发生的原因或提供有关您的代码的更多详细信息?【参考方案6】:

如果不想导入相关模型类,可以:

except MyModel.related_field.RelatedObjectDoesNotExist:

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

其中related_field 是字段名称。

【讨论】:

如果您需要避免循环导入,这实际上非常有用。谢谢

以上是关于你如何捕捉到这个异常?的主要内容,如果未能解决你的问题,请参考以下文章

python 异常处理

异常处理

ios的app无法捕捉js异常信息

JAVA如何抛出异常

python的异常处理

二十异常处理