Search code examples
djangodjango-modelsdjango-orm

Django OneToOne field where one side of the relation is mandatory


Suppose I have these models:

class Space(models.Model):
    item = models.OneToOneField(Item, null=True, on_delete=models.CASCADE)

class Item(models.model):
    ...

A Space can contain an Item, but it can be empty too. On the other hand, every Item must have a Space. It cannot exist in a void.

I could create a field .space in my Item model (with null=False), but then Space.container can never be empty (raising a RelatedObjectDoesNotExist exception when creating/querying them).

Handling these exceptions is cumbersome. I'd rather have Space.container return None (as is the case with my current setup).

Is there an easy way to make .space in the Item model mandatory, but not the other way around?


Solution

  • I think this would end up being quite hacky. Because the default of such a case is handling the exceptions. Here your Space is a Place and your Item is a Restaurant.

    I suggest going with proposed tactic: give Item a mandatory OneToOne_Field called space. Then instead of error handling go with in linked example shown alternative:

    You can also use hasattr to avoid the need for exception catching:

    space = Space.objects.all().first()
    if hasattr(space, 'item'):
        # do your logic without handling the error ObjectDoesNotExist
    

    I'd be very interested in other answers!

    On a second thought:

    You can also implement the error handling or the handling shown above via a property.

    class Space(models.Model):
        # ...
        @property
        def item(self):
            try:
                return self._item
            except ObjectDoesNotExist:
                return None
    
    class Item(models.model):
        item = models.OneToOneField(Item, null=False, on_delete=models.CASCADE, related_name='_item')
    

    Honestly I've never tried that, but why should this not work?

    Let me know how it goes!