在Django中检查是否存在一对一关系

39

我现在正在使用 Django 1.6。

我有两个模型与 OneToOneField 相关联。

class A(models.Model):
    pass

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)

首先看看我的代码,它指出了问题:

a1 = A.objects.create()
a2 = A.objects.create()
b1 = B.objects.create()
b2 = B.objects.create(ref_a=a2)

# then I call:
print(a1.ref_b)  # DoesNotExist Exception raised
print(a2.ref_b)  # returns b2
print(b1.ref_a)  # returns None
print(b2.ref_a)  # returns a2

现在的问题是,如果我想要检查一个 A 对象,来判断是否存在一个引用它的 B 对象。我应该怎么做?
我尝试过的有效方法只有尝试并捕获异常,但是否有其他更好的方式呢?
我的努力:
1 - 下面的代码可以工作,但太丑了!
b = None
try:
    b = a.ref_b
except:
    pass

2 - 我还尝试查看a标签的属性,但是没有起作用:

b = a.ref_b if hasattr(a, 'ref_b') else None

你遇到同样的问题吗,朋友们?请指点我一条路,谢谢!

我认为这对你有用,但请记得至少在2个后面更改你的类名。b = hasattr(a_class_object, 'b class') and a.ref_b or None。我正在使用相同的方法,对我很有效。 - Yogesh dwivedi Geitpl
@Yogesh,我不太明白,请您发一份更详细的解决方案好吗? - Alfred Huang
hasattr 在 Django 1.8.17 和 Python 2 上对我来说似乎工作正常。 - beruic
5个回答

50

所以你至少有两种检查的方式。 第一种是创建try/catch块来获取属性,第二种是使用hasattr

class A(models.Model):
   def get_B(self):
       try:
          return self.b
       except:
          return None

class B(models.Model):
   ref_a = models.OneToOneField(related_name='ref_b', null=True)

请尽量避免使用裸的except:语句。这可能会隐藏一些问题

第二种方式是:

class A(models.Model):
    def get_B(self):
       if(hasattr(self, 'b')):
           return self.b
       return None

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)
在这两种情况下,您都可以无需任何例外地使用它:
a1 = A.objects.create()
a2 = A.objects.create()
b1 = B.objects.create()
b2 = B.objects.create(ref_a=a2)

# then I call:
print(a1.get_b)  # No exception raised
print(a2.get_b)  # returns b2
print(b1.a)  # returns None
print(b2.a)  # returns a2

由于Django中抛出异常是默认行为,因此没有其他方式可用一对一关系

这是官方文档中处理它的示例。

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

15

单独的模型类提供了一个更具体的异常,称为DoesNotExist,它扩展了ObjectDoesNotExist。我的偏好是这样写:

个别的模型类提供了一个更具体的例外,称为DoesNotExist,它继承了ObjectDoesNotExist。我的偏好是这样写:

b = None
try:
    b = a.ref_b
except B.DoesNotExist:
    pass

7
为了节省一行代码,为什么不在except子句中加入b = None呢? - nivcaner

14

3
使用基于OneToOneField的自定义Django字段更加简洁,这种方法允许您直接访问 - 只需a.ref_b,结果将是instance or None
from django.db.models.fields.related import ReverseOneToOneDescriptor
from django.core.exceptions import ObjectDoesNotExist
from django.db import models

class SingleRelatedObjectDescriptorReturnsNone(ReverseOneToOneDescriptor):
    def __get__(self, *args, **kwargs):
        try:
            return super().__get__(*args, **kwargs)
        except ObjectDoesNotExist:
            return None

class OneToOneOrNoneField(models.OneToOneField):
    """A OneToOneField that returns None if the related object doesn't exist"""
    related_accessor_class = SingleRelatedObjectDescriptorReturnsNone

所以示例看起来会像这样

class A(models.Model):
    pass

class B(models.Model):
    ref_a = OneToOneOrNoneField(related_name='ref_b', null=True)

1

通过使用上下文库的suppress方法,可以扩展mr. Coffee的答案。

import contextlib

class A(models.Model):
    def get_B(self):
       with contextlib.suppress(Exception):
           return self.b

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接