222 votes

Comment exprimer une relation One-To-Many dans Django ?

Je suis en train de définir mes modèles Django en ce moment et j'ai réalisé qu'il n'y avait pas de OneToManyField dans les types de champs du modèle. Je suis sûr qu'il y a un moyen de le faire, donc je ne suis pas sûr de ce qui me manque. J'ai essentiellement quelque chose comme ceci :

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

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

Dans ce cas, chaque Dude peut avoir plusieurs PhoneNumber mais la relation doit être unidirectionnelle, en ce sens que je n'ai pas besoin de savoir de la part du PhoneNumber dont Dude le possède, en tant que tel, car je peux avoir de nombreux objets différents qui possèdent PhoneNumber des instances, telles qu'un Business par exemple :

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

Que remplacerais-je ? OneToManyField (qui n'existe pas) avec dans le modèle pour représenter ce type de relation ? Je viens de Hibernate/JPA où la déclaration d'une relation un-à-plusieurs était aussi simple que :

@OneToMany
private List<PhoneNumber> phoneNumbers;

Comment puis-je exprimer cela dans Django ?

180voto

rolling stone Points 4286

Pour gérer les relations One-To-Many dans Django, vous devez utiliser ForeignKey .

La documentation sur ForeignKey est très complète et devrait répondre à toutes les questions que vous vous posez :

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

La structure actuelle de votre exemple permet à chaque Mec de disposer d'un numéro et à chaque numéro d'appartenir à plusieurs Mecs (idem pour les entreprises).

Si vous souhaitez obtenir la relation inverse, vous devez ajouter deux champs ForeignKey à votre modèle PhoneNumber, un pour Dude et un pour Business. Cela permettrait à chaque numéro d'appartenir soit à un homme, soit à une entreprise, et aux hommes et aux entreprises de posséder plusieurs numéros. Je pense que c'est peut-être ce que vous recherchez.

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

3 votes

Pourriez-vous me donner un exemple compte tenu du problème ci-dessus ? Je ne comprends probablement pas du tout, mais je lis la documentation de Django depuis un certain temps déjà et je ne sais toujours pas comment créer ce type de relation.

5 votes

Il faudrait peut-être faire en sorte que les deux ForeignKeys ne soient pas obligatoires (blank=True, null=True), ou ajouter une sorte de validation personnalisée pour s'assurer qu'il y a au moins l'un ou l'autre. Qu'en est-il du cas d'une entreprise ayant un numéro générique ? ou d'un chômeur ?

1 votes

@j_syk bon point pour la validation personnalisée. mais il semble un peu compliqué d'inclure à la fois une foreignkey to dude et une foreignkey to business, puis de faire une validation personnalisée (externe à la définition du modèle). il semble qu'il doive y avoir un moyen plus propre, mais je n'arrive pas non plus à le comprendre.

49voto

stillLearning Points 128

Dans Django, une relation un-à-plusieurs est appelée ForeignKey. Cependant, elle ne fonctionne que dans un seul sens, donc plutôt que d'avoir une relation de type number attribut de la classe Dude vous aurez besoin de

class Dude(models.Model):
    ...

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

De nombreux modèles peuvent avoir un ForeignKey à un autre modèle, il serait donc valable d'avoir un deuxième attribut de PhoneNumber de telle sorte que

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

Vous pouvez accéder à la PhoneNumber s pour un Dude objet d con d.phonenumber_set.objects.all() et faire de même pour a Business objet.

3 votes

Je pensais que ForeignKey signifiait "un à un". En utilisant votre exemple ci-dessus, je devrais avoir un Dude qui a de nombreux PhoneNumbers n'est-ce pas ?

6 votes

J'ai modifié ma réponse pour refléter cela. Oui. ForeignKey est seulement un-à-un si vous spécifiez ForeignKey(Dude, unique=True) Ainsi, avec le code ci-dessus, vous obtiendrez un message de type Dude avec de multiples PhoneNumber s.

1 votes

@rolling stone- merci, j'ai ajouté cela après avoir réalisé mon erreur comme vous l'avez commenté. Unique=True ne fonctionne pas exactement comme OneToOneField, je voulais expliquer que ForeignKey utilise une relation biunivoque uniquement si vous spécifiez Unique=True.

38voto

validname Points 159

Pour être plus clair - il n'y a pas de OneToMany dans Django, seulement ManyToOne - qui est Foreignkey décrit ci-dessus. Vous pouvez décrire la relation OneToMany en utilisant Foreignkey mais c'est très inexpressif.

Un bon article à ce sujet : https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/

24voto

Vous pouvez utiliser l'une ou l'autre clé étrangère sur plusieurs côtés de OneToMany (c'est-à-dire ManyToOne ) ou utiliser ManyToMany (sur n'importe quel côté) avec une contrainte unique.

21voto

Django est assez intelligent. En fait, nous n'avons pas besoin de définir oneToMany champ. Il sera automatiquement généré par Django pour vous. Nous avons seulement besoin de définir un foreignKey dans le tableau correspondant. En d'autres termes, nous devons seulement définir ManyToOne en utilisant 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)  

Si nous voulons obtenir la liste des roues d'une voiture particulière, nous utiliserons l'objet généré automatiquement par Python. wheel_set . Pour les voitures c vous utiliserez c.wheel_set.all() .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X