[Django]-Django: Return 'None' from OneToOneField if related object doesn't exist?

12πŸ‘

βœ…

This custom django field will do exactly what you want:

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

To use it:

class Breakfast(models.Model):
    pass
    # other fields

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

breakfast = Breakfast()
assert breakfast.egg == None
πŸ‘€Bryce Drennan

9πŸ‘

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.

πŸ‘€ejucovy

5πŸ‘

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:

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
πŸ‘€OmerGertel

2πŸ‘

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

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
πŸ‘€seishin

1πŸ‘

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:

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]

and this admin definition:

class EggAdmin(admin.ModelAdmin):
    pass

class BreakfastAdmin(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)

0πŸ‘

I would recommend using try / except Egg.DoesNotExist whenever you need to access Breakfast.egg; doing so makes it very clear what’s going on for people reading your code, and this is the canonical way of handling nonexistent records in Django.

If you really want to avoid cluttering your code with try / excepts, you could define a get_egg method on Breakfast like so:

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

This will make it clearer to people reading your code that eggs are derived, and it may hint at the fact that a lookup gets performed when one calls Breakfast.get_egg().

Personally, I’d go for the former approach in order to keep things as clear as possible, but I could see why one may be inclined to use the latter approach instead.

Leave a comment