我有一个像这样的Django课程:
class Breakfast(m.Model):
# egg = m.OneToOneField(Egg)
...
class Egg(m.Model):
breakfast = m.OneToOneField(Breakfast, related_name="egg")
如果没有与breakfast.egg == None
相关的Egg
,是否可以Breakfast
?
修改:忘记提及:我不想将related_name
更改为related_name="_egg"
之类的内容,而是有类似的内容:
@property
def egg(self):
try:
return self.egg
except ...:
return None
因为我在查询中使用了名称egg
,而我不必将查询更改为使用_egg
。
答案 0 :(得分:9)
我刚遇到这个问题,并找到了一个奇怪的解决方案:如果你select_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
我不知道这是否可以被认为是一个稳定的功能。
答案 1 :(得分:8)
此自定义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
答案 2 :(得分:6)
我知道在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
答案 3 :(得分:2)
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(models.OneToOneField):
"""A OneToOneField that returns None if the related object doesn't exist"""
related_accessor_class = ReverseOneToOneOrNoneDescriptor
答案 4 :(得分:1)
OmerGertel已经指出了null
选项。但是,如果我理解您的逻辑模型正确,那么您实际需要的是从Breakfast到Egg的唯一且可以为空的外键。因此,早餐可能会或可能没有鸡蛋,特定鸡蛋只能与一份早餐相关联。
我用过这个模型:
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]
和此管理员定义:
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)
答案 5 :(得分:0)
我建议您在需要访问try
时使用except Egg.DoesNotExist
/ Breakfast.egg
;这样做非常清楚读取代码的人会发生什么,这是处理Django中不存在的记录的canonical way。
如果您真的希望避免使用try
/ except
来混淆代码,那么可以在get_egg
上定义Breakfast
方法这样:
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
这将使阅读您的代码的人更清楚地了解鸡蛋,并且可能暗示当一个人调用Breakfast.get_egg()
时执行查找。
就个人而言,我会采用前一种方法,以尽可能保持清晰,但我可以理解为什么人们倾向于使用后一种方法。