Skip to content Skip to sidebar Skip to footer

Django: Return 'none' From Onetoonefield If Related Object Doesn't Exist?

I've got a Django class like this: class Breakfast(m.Model): # egg = m.OneToOneField(Egg) ... class Egg(m.Model): breakfast = m.OneToOneField(Breakfast, related_name='

Solution 1:

This custom django field will do exactly what you want:

classSingleRelatedObjectDescriptorReturnsNone(SingleRelatedObjectDescriptor):
    def__get__(self, *args, **kwargs):
        try:
            returnsuper(SingleRelatedObjectDescriptorReturnsNone, self).__get__(*args, **kwargs)
        except ObjectDoesNotExist:
            returnNoneclassOneToOneOrNoneField(models.OneToOneField):
    """A OneToOneField that returns None if the related object doesn't exist"""
    related_accessor_class = SingleRelatedObjectDescriptorReturnsNone

To use it:

classBreakfast(models.Model):
    pass# other fieldsclassEgg(m.Model):
    breakfast = OneToOneOrNoneField(Breakfast, related_name="egg")

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

Solution 2:

I just ran into this problem, and found an odd solution to it: if you select_related(), then the attribute will be None if no related row exists, instead of raising an error.

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

I have no idea if this can be considered a stable feature though.

Solution 3:

I know that on ForeignKey you can have null=True when you want to allow the model not to point to any other model. OneToOne is only a special case of a ForeignKey:

classPlace(models.Model)
    address = models.CharField(max_length=80)
classShop(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

Solution 4:

Django 1.10 solution as by Fedor at accepted answer:

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

classReverseOneToOneOrNoneDescriptor(ReverseOneToOneDescriptor):
    def__get__(self, instance, cls=None):
        try:
            returnsuper(ReverseOneToOneOrNoneDescriptor, self).__get__(instance=instance, cls=cls)
        except ObjectDoesNotExist:
            returnNoneclassOneToOneOrNoneField(models.OneToOneField):
    """A OneToOneField that returns None if the related object doesn't exist"""
    related_accessor_class = ReverseOneToOneOrNoneDescriptor

Solution 5:

OmerGertel did already point out the null option. However, if I understand your logical model right, then what you actually need is a unique and nullable foreign key from Breakfast to Egg. So a breakfast may or may not have an egg, and a particular egg can only be associated with one breakfast.

I used this model:

classEgg(models.Model):
    quality = models.CharField(max_length=50)
    def__unicode__(self):
        return self.quality

classBreakfast(models.Model):
    dish = models.TextField()
    egg = models.ForeignKey(Egg, unique=True, null=True, blank=True)
    def__unicode__(self):
        return self.dish[:30]

and this admin definition:

classEggAdmin(admin.ModelAdmin):
    passclassBreakfastAdmin(admin.ModelAdmin):
    pass

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

Then I could create and assign an egg in the edit page for a breakfast, or just do not assign one. In the latter case, the egg property of the breakfast was None. A particular egg already assigned to some breakfast could not be selected for another one.

EDIT:

As OmerGertel already said in his comment, you could alternatively write this:

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

Post a Comment for "Django: Return 'none' From Onetoonefield If Related Object Doesn't Exist?"