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.


This is a companion discussion topic for the original entry at http://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/

Why not just use many-to-many for this?

Many to many means a different thing. Perhaps in your app domain, Musicians can only belong to one Band at a time. Or, more realistically, Musicians can only have one Agent. Using many-to-many, you'd have to enforce these restrictions outside the data model, which negates the advantage of using the ORM.

If musicians can only have one agent then a foreign key from the musician to the agent is just fine.

Absolutely. The discussion presented is not about technical capability, but semantic accuracy. There are infinite ways to implement a particular technical requirement in code, but this is more about a construct to make this aspect of Django more readable, understandable, and maintainable. As apps get huge, with lots of model objects, it because very hard to get a clear picture of the model layer when relevant info for one model is strewn about any number of other model classes based on relationships you have to know to look for.

There's a reason why OneToMany doesn't exist in Django and many other ORMs. The issue is how to represent that relationship in the database and most ORMs try to keep the database structure fairly close to how the objects actually look. There's two options for specifying the "OneToMany" relationship:

1) It's still a foreign key on Musician - which is a bad implementation since now a field/column is being defined on a table from some completely different object. When adding a new model, such as Union, one wouldn't expect the Musician table to change (or one may not even have access to change the Musician table).

2) It's a join table between Musician and Band but with a uniqueness constraint on musician_id - which is simple a ManyToMany and adding the uniqueness constraint - so it's just syntactic sugar to call it OneToMany.

With option #2, there's now an unnecessary table and joins across that table to any queries to access what should really just be a foreign key. Probably not an issue for small apps but can have a significant impact on performance as the tables get larger and the schema more complex.

The decision Django has made here is that the syntactic sugar of having the field defined in code on the One side just isn't worth setting up a more convoluted schema - especially given that related_name contributes the same attributes on the One object.

It seems you didn't read the article. It is arguing precisely the use of foreign key from musician.

I think "1)" is not necessarily a bad implementation. There are magic fields that are not in the table for a given model, i.e in a ManyToMany relationship you can consult from an object of one model the set of objects from the other related.

Nothing is free, so a measure has to be done, and the advantages of a OneToMany are big in my opinion, specially the "Extensibility".

I really support this case. The advantage in extensibility is what convince me more, which is by the way a consequence of the semantics win.

Django newbie here.

Correct me if I am wrong, but isn't a ForeignKey anyway a one-to-many relationship? Why not implement a ForeignKey called Musician in the definition of your Band model rather than a OneToMany on Musician?

class Musician(models.Model):
...

class Band(models.Model):
...
musician = ForeignKey(to=Musician, related_name="in_band", null=True, blank=True)
...

IMHO, it makes more sense to rename ForeignKey to OneToMany instead of providing a separate relationship which essentially fulfils the same need in the same space. Personally, when assigning my ForeignKeys I read them mentally as OneToMany and assign them accordingly.

Or have I completely misread the entire premise? If so, then sincere apologies for wasting your time. :)

Cheers!

This is very logical, I want to seperate my user logic from my arena logic (which may contain many users, like band.)

It's like unbelievable I'm not able to do this without altering my user model, because Django always emphasizes the modularity of apps.. How can I ship my arena app to someone, if they can't use it without altering their user model ?

"Why not implement a ForeignKey called Musician in the definition of your Band model rather than a OneToMany on Musician?"

Because a band can have more than one musician. Your solution only allows 0 or 1 musicians in a band.

Yes I strongly agree with this. A semantically correct OneToMany relationship missing in Django is the source of application layering problems.

With a simple example: when you want to write an application that uses another app for some sort of master-detail relationship (e.g. books that have pages, assuming someone wrote a pages app) you cannot properly layer one upon the other and put all the knowledge of the relationship in the "using" app (books), but you have to leak some of that knowledge to the "used" app (pages), thus breaking the principle of layering (which is the source of evil cyclic dependencies [1]) and making it also far less reusable in other contexts.

So I think we need a OneToMany relationship that we can declare in the using end of the relationship that is internally implemented as a foreign key in the other end.

I think the reason this is not implemented in Django (nor some third party package I know of) is because it is harder to do than the other case, as you cannot generate the DB schema for one app (syndcb, migrate) in isolation from the whole project context. But I'd love to see if someone has tackled that problem, and lend her a hand.

[1] Django can solve the problem of cyclic dependencies with lazy foreign key lookups (using a string with the model name instead of a class) but semantically that looks to me like a bit of a hack for this case in question

Support this case.

It's been a long time, but i feel like jumping in.

"Correct me if I am wrong, but isn't a ForeignKey anyway a one-to-many relationship?"

actually, foreign keys are NOT One-To-Many relationships, they are Many-To-One. If you want a One-To-Many you have to use a Foreign Key in the opposite side of the relation.

Yes!! This is exactly what I´ve been looking for :-) And I really like the separation of the semantic vs technical solutions to a problem. I think the semantic part gets far too little attention. Anyway, has anyone found a solution for the one-to-many relationship in Django yet? (And by solution, even though technically valid, I don´t mean the reverse ForeignKey approach...)

If you have Order and OrderItem models, your order FK is in OrderItem model, from OrderItem model point of view, it's Many OrderItem to One Order, in other words ManyToOne. What this implies is you need to have Order records before OrderItem records.

What if you have 2 different apps, say Payment and Settlement, which a payment can have multiple settlements, but settlements can only belong to one payment. Logically Payment app is built on top of Settlement app, in other words Settlement app doesn't need to know about Payment app. But to make the constraint to work, you need to put the Payment foreignkey into the Settlement model (surely this makes sense in DB schema, but not in programming domain), which now Settlement app is required to know about the Payment app.

Not only the dependency is reversed, now you need to have payment record created before settlement record, WTF? Oh certainly you have the payment FK set to nullable, but then you will have an extra step to set the relationship.

I used this one https://github.com/adsworth/dj... and seems to work fine with 1.7 as well. It's not in pypy tho so you will have to copy the code to your project for the time being.

What it does is still a manytomany field but has the right side unique constraint.

Now be aware that if you ever want to roll back to the normal manytomany field, the builtin migration does not remove the additional unique constraints introduced by this package.

I'm late to the party, but this is one of the top hits on google for 'django one to many', and I'd like to give my endorsement to this idea.

The system I'm currently modeling has a table for recipes and a table for ingredients. Ostensibly, this sounds like a shining example of a many-to-many relationship. However, each ingredient in a recipe has a quantity associated with it, and each recipe can only contain up to four ingredients.

Currently, my only option is to manually create an intermediary table to use with a ManyToManyField and store the quantity info there. As above, this isn't really a problem, technically, but it certainly needlessly obfuscates the semantics. If I had a one-to-many relationship, I could simply declare ingredient1, quantity1, ingredient2, etc. in the recipe table and not have to manually create the additional table. Simply declaring foreign keys on the ingredients doesn't work because that gives me no way to associate quantities with them in the recipe.

Maybe there's some magic way to dictate the order of what's returned via the reverse lookup using a custom reverse manager, but that feels a lot like over-engineering.

I think you have a typo. You say "However, when adding a many-to-one relationship, you must always
place the ForeignKey in the 'one' side,..." but I think you should say "... in the 'many' side,...".