web-dev-qa-db-fra.com

Comment exprimer une relation un à plusieurs dans Django

Je définis actuellement mes modèles Django et je me suis rendu compte qu'il n'y avait pas de OneToManyField dans les types de champ de modèle. Je suis sûr qu'il existe un moyen de faire cela, donc je ne suis pas sûr de ce qu'il me manque. J'ai essentiellement quelque chose comme ça:

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

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

Dans ce cas, chaque Dude peut avoir plusieurs PhoneNumbers, mais la relation doit être unidirectionnelle, en ce sens que je n'ai pas besoin de savoir à partir de PhoneNumber laquelle Dude le possède , comme tel, j’aurais peut-être beaucoup d’objets différents qui possèdent des instances de PhoneNumber, comme un Business, par exemple:

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

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

@OneToMany
private List<PhoneNumber> phoneNumbers;

Comment puis-je exprimer cela dans Django? 

125
Naftuli Kay

Pour gérer les relations un-à-plusieurs 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/dev/ref/models/fields/#foreignkey

La structure actuelle de votre exemple autorise chaque Dude à avoir un numéro et chaque numéro à appartenir à plusieurs Dudes (identique à Business). 

Si vous voulez la relation inverse, vous devez ajouter deux champs ForeignKey à votre modèle PhoneNumber, un à Dude et un à Business. Cela permettrait à chaque numéro d'appartenir à un type ou à une entreprise et d'avoir des types et des entreprises capables de posséder plusieurs numéros. Je pense que cela pourrait ê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)
97
rolling stone

Dans Django, une relation un-à-plusieurs est appelée ForeignKey. Cependant, cela ne fonctionne que dans un sens. Ainsi, plutôt que d'avoir un attribut number de 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 valide d'avoir un deuxième attribut de PhoneNumber tel 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 à PhoneNumbers pour un objet Duded avec d.phonenumber_set.objects.all(), puis procéder de la même manière pour un objet Business.

34
stillLearning

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

Pour être plus clair - il n'y a pas de OneToMany dans Django, mais de ManyToOne - qui est la clé Foreignkey décrite ci-dessus. Vous pouvez décrire la relation OneToMany à l’aide de 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/

8
validname

Django est assez intelligent. En réalité, nous n'avons pas besoin de définir le champ oneToMany. Il sera généré automatiquement par Django pour vous :-). Nous avons seulement besoin de définir foreignKey dans la table liée. En d'autres termes, il suffit de définir la relation ManyToOne à l'aide de 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 allons utiliser python's objet généré automatiquement wheel_set. Pour voiture c vous utiliserez c.wheel_set.all()

2
Sheikh Abdul Wahid

Si le modèle "many" ne justifie pas la création d'un modèle en soi (ce qui n'est pas le cas ici, mais cela pourrait profiter à d'autres personnes), une autre alternative serait de s'appuyer sur des types de données PostgreSQL spécifiques, via le package Django Contrib

Postgres peut traiter les types de données Array ou JSON , ce qui peut constituer une solution de contournement intéressante pour la gestion de One-To-Many lorsque les plusieurs-fichiers ne peuvent être liés une seule entité du un

Postgres vous permet d'accéder à des éléments uniques du tableau, ce qui signifie que les requêtes peuvent être très rapides et éviter les frais généraux au niveau de l'application. Et bien sûr, Django implémente une API géniale pour exploiter cette fonctionnalité.

Il a évidemment l’inconvénient de ne pas être portable sur d’autres bases de données, mais j’estime que cela mérite d’être mentionné.

J'espère que cela aidera certaines personnes à la recherche d'idées.

0
edouardtheron

Bien que la réponse de rolling stone soit bonne, simple et fonctionnelle, je pense qu'il y a deux choses que cela ne résout pas.

  1. Si OP souhaitait imposer un numéro de téléphone, il ne pouvait appartenir à la fois à un homme et à une entreprise.
  2. Le sentiment de tristesse inévitable résultant de la définition de la relation sur le modèle PhoneNumber et non sur les modèles Dude/Business. Lorsque des extraterrestres viennent sur Terre et que nous voulons ajouter un modèle Alien, nous devons modifier le numéro de téléphone (en supposant que les ET aient des numéros de téléphone) au lieu d'ajouter simplement un champ "phone_numbers" au modèle Alien.

Introduisez le framework de types de conten , qui expose certains objets nous permettant de créer une "clé étrangère générique" sur le modèle PhoneNumber. Ensuite, nous pouvons définir la relation inverse sur 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)

Voir le docs pour plus de détails, et peut-être consulter cet article pour un tutoriel rapide.

En outre, voici n article qui défend contre l'utilisation de FK génériques.

0
rabeye