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 ?

13voto

rabeye Points 106

Alors que pierre roulante La réponse de l'UE est bonne, directe et fonctionnelle, mais il y a deux choses qu'elle ne résout pas.

  1. Si l'OP voulait faire respecter le fait qu'un numéro de téléphone ne peut pas appartenir à la fois à un homme et à une entreprise
  2. L'inévitable sentiment de tristesse 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 modèle PhoneNumber (en supposant que les ETs ont des numéros de téléphone) au lieu d'ajouter simplement un champ "phone_numbers" au modèle Alien.

Présentez le cadre des types de contenu qui expose certains objets qui nous permettent 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 et 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.

Voici également un article qui argumente contre l'utilisation de FKs génériques.

4voto

reborn Points 11

Tout d'abord, nous faisons une visite guidée :

01) relation de type "un à plusieurs" :

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 ne fournit pas de relation OneToMany. Nous ne pouvons donc pas utiliser la méthode upper dans Django. Mais nous avons besoin de convertir en modèle relationnel. Alors que pouvons-nous faire ? Dans cette situation, nous devons convertir le modèle relationnel en modèle relationnel inverse.

Ici :

modèle relationnel = OneToMany

Donc, modèle relationnel inverse = ManyToOne

NB : Django supporte la relation ManyToOne & dans Django ManyToOne est représenté par ForeignKey.

02) relation de plusieurs à un :

SOLVE:

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

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

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

NB : PENSEZ SIMPLEMENT !!

0voto

edouardtheron Points 304

Si le modèle "many" ne justifie pas la création d'un modèle en soi (ce 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 la fonction Paquet Django Contrib

Postgres peut traiter Array ou JSON et cela peut être une solution de contournement intéressante pour gérer le One-To-Many lorsque les types de données du many-ies ne peut être liée qu'à une seule entité de l'UE. 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 surcharges au niveau de l'application. Et bien sûr, Django implémente une API sympa pour tirer parti de cette fonctionnalité.

Il a évidemment l'inconvénient de ne pas être portable à d'autres bases de données, mais je pensais qu'il valait la peine d'être mentionné.

J'espère que cela pourra aider certaines personnes en quête d'idées.

0voto

Ken Points 180

En fait, la relation un-à-plusieurs est très utile. Je fais quelque chose comme ça :

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(...)

Ainsi, le numéro ne sera utilisé qu'une seule fois.

0voto

MD SHAYON Points 93

Une relation de type "un à plusieurs" implique qu'un enregistrement de modèle peut être associé à plusieurs autres enregistrements de modèle.

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)

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