A Case for a OneToMany Relationship in Django

There are three types of model relationships that Django provides: many-to-one, many-to-many and one-to-one. In the following post I intend to make a case for adding a one-to-many relationship to this list.

Obviously, the immediate opposition to this proposal is that all relationships in Django are provided in a two-way manner (via related_name), so there’s no need for it. Let’s take a look at two reasons why I think this new relationship is called for.

Semantics

The many-to-one relationship that Django provides is ForeignKey. A foreign key is always declared on the “one” side of the relationship, e.g., if you have a Band model and each Band has several Musician models related to it, then you would put a ForeignKey in the Musician model like so:

class Band(models.Model):

    # ...
    
class Musician(models.Model):
    # ..
    band = ForeignKey(to=Band, related_name="members", null=True, blank=True)

This way, every musician has a band related to it, and bands may have musicians related to them, but this is the wrong way around! Musicians exist without bands. Some are in bands, some aren’t. Even those who are in a band aren’t defined by it. However, a band is determined by its members. A band without members cannot exist. So, there’s a semantic issue here. Therefore, I suggest the following syntax:

class Band(models.Model):
    # ...
    members = OneToMany(to=Musician)
    
class Musician(models.Model):
    # ..

This way - every band has musicians. Musicians may be in bands, but not necessarily. And, and this is the difference between the proposed OneToMany and the already-existing ManyToMany, each musician is in only one band.

Readability

Let’s toss some more models into the mix. Suppose we have music agents and each one of them has musicians as clients. Perhaps we also have different musician unions. Using ForeignKey again, it’ll look like this:

class Band(models.Model):
    # ...
    
class Agent(models.Model):
    # ...
    
class Union(models.Model):
    # ...
    
class Musician(models.Model):
    # ..
    band = ForeignKey(to=Band, related_name="members", null=True, blank=True)
    agent = ForeignKey(to=Agent, related_name="clients", null=True, blank=True)
    union = ForeignKey(to=Union, related_name="members", null=True, blank=True)

Again, for Agent and Union (like Band before), it’s confusing that a required piece of information about them is defined elsewhere, while in Musician there are so many optional members that clutter up what’s important about the musician. Here’s how it’ll look like with OneToMany relationships:

class Band(models.Model):
    # ...
    members = OneToMany(to=Musician)
    
class Agent(models.Model):
    # ...
    clients = OneToMany(to=Musician)
    
class Union(models.Model):
    # ...
    members = OneToMany(to=Musician)
    
class Musician(models.Model):
    # ..

Every model contains all of its required information and at the same time they are not cluttered with a bunch of optional members. With ForeignKey, the more “musician groups” we have, the more clutter is gathered in the Musician model. So OneToMany helps keep the information where it has the most meaning to you, the programmer.

Extensibility

The previous example also suggests another advantage of the relationship. When working with an already-existing model, both OneToOne and ManyToMany relationships allow us to add information to that model without altering its source code. However, when adding a many-to-one relationship, you must always place the ForeignKey in the “one” side, which may be a model which you can’t change, for any number of reasons (e.g., working with a library, or working with code that is common to several applications). The OneToMany relationship allows us to break that barrier.

Discuss this post at the comment section below.
Follow me on Twitter , Facebook or Google+

Similar Posts