Bagaimana Anda menangkap pengecualian ini?

162

Kode ini ada di django / db / models / fields.py Ini membuat / mendefinisikan pengecualian?

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

Ini ada di django / db / model / bidang / related.py itu menimbulkan pengecualian di atas:

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

Masalahnya adalah kode ini:

    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

tidak akan menangkap pengecualian itu

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

dan

except related.RelatedObjectDoesNotExist:

Meningkatkan sebuah 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

mungkin itu sebabnya.

pembuat perahu
sumber
Bagaimana cara Anda mengimpor related?
John Zwinck
4
gunakan AttributeErrorsebagai gantinyarelated.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist
catherine
Ya @JohnZwinck saya telah mengimpor terkait.
boatcoder

Jawaban:

302

Jika model terkait Anda disebut Foo, Anda bisa melakukannya:

except Foo.DoesNotExist:

Django luar biasa ketika itu tidak menakutkan. RelatedObjectDoesNotExistadalah properti yang mengembalikan tipe yang ditemukan secara dinamis saat runtime. Tipe itu digunakan self.field.rel.to.DoesNotExistsebagai kelas dasar. Menurut dokumentasi Django:

ObjectDoesNotExist dan DoesNotExist

pengecualian DoesNotExist

The DoesNotExist eksepsi dimunculkan ketika suatu objek tidak ditemukan parameter yang diberikan dari query. Django memberikan pengecualian DoesNotExist sebagai atribut dari setiap kelas model untuk mengidentifikasi kelas objek yang tidak dapat ditemukan dan untuk memungkinkan Anda menangkap kelas model tertentu dengan try/except .

Inilah keajaiban yang membuat itu terjadi. Setelah model dibangun, self.field.rel.to.DoesNotExistadalah pengecualian tidak-tidak-ada untuk model itu.

tdelaney
sumber
7
Karena bug itu ada di DjangoRestFramework, dan modelnya agak sulit didapat pada saat itu. Saya memutuskan untuk menangkap ObjectDoesNotExist.
boatcoder
3
Anda juga dapat menggunakan AttributeError, yang dalam keadaan tertentu mungkin merupakan opsi yang lebih baik (kesalahan ini hampir selalu terjadi ketika Anda mengakses "atribut" catatan, jadi dengan cara ini Anda tidak harus melacak apakah atribut ini sesuai dengan merekam atau tidak
Jordan Reiter
1
Yaaaa baiklah. Tetapi mengapa itu tidak bisa kembali begitu saja? Apalagi dengan bidang satu-ke-satu. Atau ada alasan bagus?
Neil
61

Jika Anda tidak ingin mengimpor kelas model terkait, Anda dapat:

except MyModel.related_field.RelatedObjectDoesNotExist:

atau

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

dimana related_fieldnama fieldnya

Fush
sumber
7
ini sebenarnya cukup berguna jika Anda perlu menghindari impor melingkar. Terima kasih
Giovanni Di Milia
40

Untuk menangkap pengecualian ini secara umum, Anda bisa melakukannya

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception
Zags
sumber
2
Saya menemukan ini sebenarnya tidak menangkap kesalahan seperti yang diharapkan. Yang <Model>.DoesNotExistmelakukan
Eric Blum
1
@EricBlum <Model> .DoesNotExist adalah turunan dari ObjectDoesNotExist, sehingga seharusnya tidak terjadi. Bisakah Anda mencari tahu mengapa itu terjadi atau memberikan rincian lebih lanjut tentang kode Anda?
Zags
10

The RelatedObjectDoesNotExistpengecualian dibuat secara dinamis pada saat runtime. Berikut ini cuplikan kode yang relevan untuk ForwardManyToOneDescriptordan ReverseOneToOneDescriptordeskriptor:

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

Jadi pengecualian ini berasal dari <model name>.DoesNotExistdan AttributeError. Faktanya, MRO lengkap untuk tipe pengecualian ini adalah:

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

Dasar pengambilannya adalah Anda dapat menangkap <model name>.DoesNotExist, ObjectDoesNotExist(mengimpor dari django.core.exceptions) atau AttributeError, apa pun yang paling masuk akal dalam konteks Anda.

CS
sumber
2

Jawaban tdelaney sangat bagus untuk jalur kode reguler, tetapi jika Anda perlu tahu cara menangkap pengecualian ini dalam tes:

from django.core.exceptions import ObjectDoesNotExist

...

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

Sedikit terlambat tetapi bermanfaat untuk orang lain.

2 cara untuk menangani ini.

1:

Ketika kita perlu menangkap pengecualian

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

2: Kapan tidak ingin menangani pengecualian

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