258

I'm defining my Django models right now and I realized that there wasn't a OneToManyField in the model field types. I'm sure there's a way to do this, so I'm not sure what I'm missing. I essentially have something like this:

class Dude(models.Model):
    # 1 dude can have 0+ phone numbers
    numbers = models.OneToManyField('PhoneNumber')

class PhoneNumber(models.Model):
    number = models.CharField()

In this case, each Dude can have multiple PhoneNumbers, but the relationship should be unidirectional, in that I don't need to know from the PhoneNumber which Dude owns it, per se, as I might have many different objects that own PhoneNumber instances, such as a Business for example:

class Business(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

What would I replace OneToManyField (which doesn't exist) with in the model to represent this kind of relationship? I'm coming from Hibernate/JPA where declaring a one-to-many relationship was as easy as:

@OneToMany
private List<PhoneNumber> phoneNumbers;

How can I express this in Django?

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411

11 Answers11

207

To handle One-To-Many relationships in Django you need to use ForeignKey.

The documentation on ForeignKey is very comprehensive and should answer all the questions you have:

https://docs.djangoproject.com/en/3.2/ref/models/fields/#foreignkey

The current structure in your example allows each Dude to have one number, and each number to belong to multiple Dudes (same with Business).


If you want the reverse relationship, you would need to add two ForeignKey fields to your PhoneNumber model, one to Dude and one to Business. This would allow each number to belong to either one Dude or one Business, and have Dudes and Businesses able to own multiple PhoneNumbers. I think this might be what you're after:

class Business(models.Model):
    ...

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)
Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
rolling stone
  • 12,668
  • 9
  • 45
  • 63
  • 6
    Could you give me an example given the problem above? I'm probably just missing it completely, but I've been reading the Django documentation for some time now and am still unclear as to how to create this kind of relationship. – Naftuli Kay Aug 03 '11 at 15:44
  • 5
    might want to make both of the ForeignKeys not required (blank=True, null=True), or add some sort of custom validation to make sure that there is at least one or the other. what about the case of a business having a generic number? or an unemployed dude? – j_syk Aug 03 '11 at 23:08
  • 1
    @j_syk good point about custom validation. but it seems kind of hacky to include both a foreignkey to dude and a foreignkey to business, and then do custom (external to the model definition) validation. seems like there has to be a cleaner way, but I can't figure it out either. – andy Jun 12 '13 at 15:34
  • In this situation it is appropriate to use the [ContentTypes Framework](https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/) which allows for generic relationships to different objects. However, using content types can get very complex very quickly, and if this is your only need, this simpler (if hacky) approach might be desirable. – kball Oct 08 '13 at 17:01
  • 91
    One relevant thing to mention is the "related_name" argument to ForeignKey. So in the PhoneNumber class you'd have `dude = models.ForeignKey(Dude, related_name='numbers')` and then you can use `some_dude_object.numbers.all()` to get at all the related numbers (if you don't specify a "related_name" it'll default to "number_set"). – markshep Jul 08 '14 at 08:01
  • 1
    Is it possible to modify this so that the number can belong to either a Business or a Dude, but not both at the same time? I am also wondering if it is easy to implement a switch of the number from Business to Dude and vice versa. – MadPhysicist Dec 04 '16 at 20:02
  • @markshep, something like `item.comments.all()` works from python code BUT when I do `{{ object.comments.all() }}` in templates it says `Could not parse the remainder: '()' from 'object.comments.all()'` any extra config is required? – Srinath Ganesh Nov 08 '18 at 05:52
  • Wow, 10 years later and this answer is still helpful. Thank you! As an extra note, I battled to get these relationships displaying in my templates but found that you can't call functions inside templates e.g. `model_set.count()`. Need to do that in the view then pass it to the template. –  Jul 23 '21 at 06:55
59

In Django, a one-to-many relationship is called ForeignKey. It only works in one direction, however, so rather than having a number attribute of class Dude you will need

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)

Many models can have a ForeignKey to one other model, so it would be valid to have a second attribute of PhoneNumber such that

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

You can access the PhoneNumbers for a Dude object d with d.phonenumber_set.objects.all(), and then do similarly for a Business object.

stillLearning
  • 723
  • 6
  • 9
  • 3
    I was under the assumption that `ForeignKey` meant "one-to-one." Using your above example, I should have a `Dude` that has many `PhoneNumbers` right? – Naftuli Kay Aug 03 '11 at 15:46
  • 7
    I edited my answer to reflect this. Yes. `ForeignKey` is only one-to-one if you specify `ForeignKey(Dude, unique=True)`, so with the above code you will get a `Dude` with multiple `PhoneNumber`s. – stillLearning Aug 03 '11 at 16:00
  • 1
    @rolling stone- thank you, I was adding that after realizing my mistake as you commented. Unique=True doesn't work exactly like OneToOneField, I meant to explain that ForeignKey only uses a one-to-one relationship if you specify Unique=True. – stillLearning Aug 03 '11 at 16:14
  • You're right, that was a confusing way to clarify. I intended to modify rather than rewrite the models in the original question, but it makes more sense to show just the more useful answer. Fixed. – stillLearning Aug 03 '11 at 16:29
  • 15
    +1 for doing it from the `PhoneNumber`. Now it's starting to make sense. `ForeignKey` is essentially many-to-one, so you need to do it backwards to get a one-to-many :) – Naftuli Kay Aug 03 '11 at 16:35
  • @rolling stone, sure if you have $50 ;) – Naftuli Kay Aug 03 '11 at 16:46
  • 4
    Can somebody explain the significance of the name of the field `phonenumber_set`? I don't see it defined anywhere. Is it the name of the model, in all lower case, appended with "_set" ? – James Wierzba Dec 13 '15 at 21:46
  • @JamesWierzba It is the [related_name](https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_name) of the foreign key. There's some documentation on this in [backwards-related objects](https://docs.djangoproject.com/en/1.10/topics/db/queries/#backwards-related-objects) that explains these defaults. – Taywee Mar 15 '17 at 17:29
  • Does it protect us against a Dude and a Business sharing the same phone number? – Sergey Orshanskiy Nov 11 '18 at 21:17
  • Note that the property name inside the class must have the same name as the foreign class and it's table name and key. So in my case, I tried something like `mydude = models.ForeignKey(Dude)` and django/python didn't complain about anything, yet it wouldn't return the objects, because it was trying to select the DB using the "primary key" of `mydude_id` instead of `dude_id`. Apparently the framework is not smart enough to get the DB primary key name from the Dude class. :( – msb Nov 05 '20 at 20:51
  • There is no one-to-many. That's why you used many-to-many instead. – ar2015 Mar 19 '22 at 09:51
46

To be more clear - there's no OneToMany in Django, only ManyToOne - which is Foreignkey described above. You can describe OneToMany relation using Foreignkey but that is very inexpressively.

A good article about it: https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/

validname
  • 1,376
  • 15
  • 22
31

Django is smart enough. Actually we don't need to define oneToMany field. It will be automatically generated by Django for you. We only need to define a foreignKey in the related table. In other words, we only need to define ManyToOne relation by using foreignKey.

class Car(models.Model):
    # wheels = models.oneToMany() to get wheels of this car [**it is not required to define**].


class Wheel(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)  

If we want to get the list of wheels of particular car, we will use Python's auto generated object wheel_set. For car c you will use c.wheel_set.all().

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Sheikh Abdul Wahid
  • 2,623
  • 2
  • 25
  • 24
  • 1
    Can also modify auto-generated names with `ForeignKey(related_name=...)` – serg06 Apr 02 '21 at 01:21
  • 1
    I'd vote [this comment](https://stackoverflow.com/users/5090928/serg06) as the correct answer. What you need is to set `related_name` to your field definition. – abmap Oct 16 '22 at 18:42
27

You can use either foreign key on many side of OneToMany relation (i.e. ManyToOne relation) or use ManyToMany (on any side) with unique constraint.

metasim
  • 4,793
  • 3
  • 46
  • 70
16

While rolling stone's answer is good, straightforward and functional, I think there are two things it does not solve.

  1. If OP wanted to enforce a phone number cannot belong to both a Dude and a Business
  2. The inescapable feeling of sadness as a result of defining the relationship on the PhoneNumber model and not on the Dude/Business models. When extra terrestrials come to Earth, and we want to add an Alien model, we need to modify the PhoneNumber (assuming the ETs have phone numbers) instead of simply adding a "phone_numbers" field to the Alien model.

Introduce the content types framework, which exposes some objects that allow us to create a "generic foreign key" on the PhoneNumber model. Then, we can define the reverse relationship on Dude and Business

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

class PhoneNumber(models.Model):
    number = models.CharField()

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    owner = GenericForeignKey()

class Dude(models.Model):
    numbers = GenericRelation(PhoneNumber)

class Business(models.Model):
    numbers = GenericRelation(PhoneNumber)

See the docs for details, and perhaps check out this article for a quick tutorial.

Also, here is an article that argues against the use of Generic FKs.

rabeye
  • 176
  • 1
  • 6
4

First of all we take a tour:

01) one-to-many relationship:

ASSUME:

class Business(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class Dude(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=20)
    ........
    ........

NB: Django doesn't provides any OneToMany relationship. So we can't use upper method in Django. But we need to convert in relational model. So what can we do? In this situation we need to convert relational model into reverse relational model.

Here:

relational model = OneToMany

So, reverse relational model = ManyToOne

NB: Django support ManyToOne relationship & in Django ManyToOne is represented by ForeignKey.

02) many-to-one relationship:

SOLVE:

class Business(models.Model):
    .........
    .........

class Dude(models.Model):
    .........
    .........

class PhoneNumber(models.Model):
    ........
    ........
    business = models.ForeignKey(Business)
    dude = models.ForeignKey(Dude)

NB: THINK SIMPLY!!

iamdipta
  • 346
  • 3
  • 7
3

Actually, one-to-many relationship is very useful. I do something like this:

class Dude(models.Model):
    number = models.ManyToManyField(PhoneNumber)

    def save(self, *args, **kwargs):
        if Dude.objects.get(number=self.number):
            raise Exception("Dude, this number has been used.")
        return super(Dude, self).save(*args, **kwargs)

class PhoneNumber(models.Model):
    number = models.CharField(...)

This will ensure the number only use once.

Ken
  • 1,234
  • 10
  • 16
1

Whereas most answers here do pass in defining a relationship that allows you to define a OneToManyRelationship, they are actually in fact the definition of a ManyToManyRelationship.

Here is a more accurate implementation:

class Dude(models.Model):
    name = models.CharField(max_length=256, unique=True)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=256, unique=True)

class DudePhoneNumbers(models.Model):
    dude = models.ForeignKey(Dude, related_name="phone_number_relations", on_delete=models.CASCADE)
    phone_number = models.OneToOneField(PhoneNumber, related_name="dude_relation", on_delete=models.CASCADE)
shailysangwan
  • 108
  • 1
  • 9
0

If the "many" model does not justify the creation of a model per-se (not the case here, but it might benefits other people), another alternative would be to rely on specific PostgreSQL data types, via the Django Contrib package

Postgres can deal with Array or JSON data types, and this may be a nice workaround to handle One-To-Many when the many-ies can only be tied to a single entity of the one.

Postgres allows you to access single elements of the array, which means that queries can be really fast, and avoid application-level overheads. And of course, Django implements a cool API to leverage this feature.

It obviously has the disadvantage of not being portable to others database backend, but I thougt it still worth mentionning.

Hope it may help some people looking for ideas.

edthrn
  • 1,052
  • 12
  • 17
0

A one to many relationship implies that one model record can have many other model records associated with itself.

from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length=30)

class Item(models.Model):
    menu = models.ForeignKey(Menu)
    name = models.CharField(max_length=30)
    description = models.CharField(max_length=100)
MD SHAYON
  • 7,001
  • 45
  • 38