Django:如果相关对象不存在,从OneToOneField返回“None”?

2eafrhcq  于 2023-10-21  发布在  Go
关注(0)|答案(6)|浏览(146)

我有一个类似这样的Django类:

class Breakfast(m.Model):
    # egg = m.OneToOneField(Egg)
    ...

class Egg(m.Model):
    breakfast = m.OneToOneField(Breakfast, related_name="egg")

如果没有与Breakfast相关的Egg,是否可能有breakfast.egg == None

编辑:忘了提:我宁愿不把related_name改为类似related_name="_egg"的东西,然后有这样的东西:

@property
def egg(self):
    try:
        return self.egg
    except ...:
        return None

因为我在查询中使用名称egg,我不想将查询更改为使用_egg

j1dl9f46

j1dl9f461#

这个自定义的django字段将完全按照你的要求执行:

class SingleRelatedObjectDescriptorReturnsNone(SingleRelatedObjectDescriptor):
    def __get__(self, *args, **kwargs):
        try:
            return super(SingleRelatedObjectDescriptorReturnsNone, self).__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 Breakfast(models.Model):
    pass
    # other fields

class Egg(m.Model):
    breakfast = OneToOneOrNoneField(Breakfast, related_name="egg")

breakfast = Breakfast()
assert breakfast.egg == None
zte4gxcn

zte4gxcn2#

我刚刚遇到了这个问题,并找到了一个奇怪的解决方案:如果选择_related(),则如果不存在相关行,则属性将为None,而不是引发错误。

>>> print Breakfast.objects.get(pk=1).egg
Traceback (most recent call last):
...
DoesNotExist: Egg matching query does not exist

>>> print Breakfast.objects.select_related("egg").get(pk=1).egg
None

我不知道这是否可以被认为是一个稳定的功能。

xlpyo6sf

xlpyo6sf3#

我知道在ForeignKey上,当你想让模型不指向任何其他模型时,你可以使用null=True。OneToOne只是ForeignKey的一个特例:

class Place(models.Model)
    address = models.CharField(max_length=80)
class Shop(models.Model)
    place = models.OneToOneField(Place, null=True)
    name = models.CharField(max_length=50)
    website = models.URLField()

>>>s1 = Shop.objects.create(name='Shop', website='shop.com')
>>>print s1.place
None
bksxznpy

bksxznpy4#

Django 1.10解决方案由Fedor在接受的答案:

from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields.related import OneToOneField
from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor

class ReverseOneToOneOrNoneDescriptor(ReverseOneToOneDescriptor):
    def __get__(self, instance, cls=None):
        try:
            return super(ReverseOneToOneOrNoneDescriptor, self).__get__(instance=instance, cls=cls)
        except ObjectDoesNotExist:
            return None

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

nwo49xxi5#

OmerGertel已经指出了null选项。然而,如果我正确理解了你的逻辑模型,那么你实际上需要的是一个从早餐到鸡蛋的 * 唯一且可空的 * 外键。所以一份早餐可能有也可能没有鸡蛋,一个特定的鸡蛋只能与一份早餐联系起来。
我使用了这个模型:

class Egg(models.Model):
    quality = models.CharField(max_length=50)
    def __unicode__(self):
        return self.quality

class Breakfast(models.Model):
    dish = models.TextField()
    egg = models.ForeignKey(Egg, unique=True, null=True, blank=True)
    def __unicode__(self):
        return self.dish[:30]

这个admin定义:

class EggAdmin(admin.ModelAdmin):
    pass

class BreakfastAdmin(admin.ModelAdmin):
    pass

admin.site.register(Egg, EggAdmin)
admin.site.register(Breakfast, BreakfastAdmin)

然后我可以在编辑页面中创建并分配一个鸡蛋作为早餐,或者干脆不分配。在后一种情况下,早餐的鸡蛋属性是无。一个特定的鸡蛋已经分配给一些早餐不能选择另一个。

编辑:

正如OmerGertel在他的评论中所说,你也可以这样写:

egg = models.OneToOneField(Egg, null=True, blank=True)
yebdmbv4

yebdmbv46#

我建议在需要访问Breakfast.egg时使用try/except Egg.DoesNotExist;这样做可以让阅读你代码的人清楚地知道发生了什么,这是Django处理不存在记录的规范方法。
如果你真的不想让你的代码被try/except s弄得一团糟,你可以在Breakfast上定义一个get_egg方法,如下所示:

def get_egg(self):
    """ Fetches the egg associated with this `Breakfast`.

    Returns `None` if no egg is found.
    """
    try:
        return self.egg
    except Egg.DoesNotExist:
        return None

这将使阅读您的代码的人更清楚地知道eggs是派生的,并且它可能暗示当调用Breakfast.get_egg()时执行查找的事实。
就我个人而言,我会选择前一种方法,以使事情尽可能清晰,但我可以理解为什么人们可能倾向于使用后一种方法。

相关问题