523 votes

Quelle est la différence entre django OneToOneField et ForeignKey ?

Quelle est la différence entre Django OneToOneField y ForeignKey ?

620voto

Matthew Rankin Points 71628

Soyez attentif au fait qu'il existe certaines différences entre OneToOneField(SomeModel) y ForeignKey(SomeModel, unique=True) . Comme indiqué dans Le guide définitif de Django :

OneToOneField

Une relation biunivoque. Conceptuellement, elle est similaire à une ForeignKey con unique=True mais le côté "inverse" de la relation renverra directement un objet unique.

Contrairement à la OneToOneField relation "inverse", une ForeignKey La relation "reverse" renvoie un QuerySet .

Exemple

Par exemple, si nous avons les deux modèles suivants (code complet du modèle ci-dessous) :

  1. Car le modèle utilise OneToOneField(Engine)
  2. Car2 le modèle utilise ForeignKey(Engine2, unique=True)

De l'intérieur python manage.py shell exécutez ce qui suit :

OneToOneField Exemple

>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car
<Car: Audi>

ForeignKey con unique=True Exemple

>>> from testapp.models import Car2, Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all()
[<Car2: Mazda>]

Code du modèle

from django.db import models

class Engine(models.Model):
    name = models.CharField(max_length=25)

    def __unicode__(self):
        return self.name

class Car(models.Model):
    name = models.CharField(max_length=25)
    engine = models.OneToOneField(Engine)

    def __unicode__(self):
        return self.name

class Engine2(models.Model):
    name = models.CharField(max_length=25)

    def __unicode__(self):
        return self.name

class Car2(models.Model):
    name = models.CharField(max_length=25)
    engine = models.ForeignKey(Engine2, unique=True, on_delete=models.CASCADE)

    def __unicode__(self):
        return self.name

5 votes

@MarkPNeyer : d'après ce que j'ai compris, un champ OneToOne n'est que cela : one-to-one. Il ne doit pas nécessairement être sur. Voir cet exemple : un lieu ne doit pas nécessairement être un restaurant.

31 votes

Cette réponse dit "il y a quelques différences", puis nomme une différence. Y en a-t-il d'autres ?

6 votes

Je me pose la même question que Chris. S'agit-il simplement d'un sucre syntaxique, y a-t-il une différence sous-jacente dans la façon d'accéder aux données, ce qui entraîne des différences de performances ?

161voto

Dan Breen Points 4381

A ForeignKey est une relation de plusieurs à un. Ainsi, un Car peut avoir de nombreuses instances de Wheel . Chaque Wheel aurait donc un ForeignKey à la Car à laquelle il appartient. A OneToOneField serait comme une instance de Engine où a Car ne peut avoir qu'un seul et unique objet.

6 votes

Merci, Est-ce que OneToOneField(someModel) signifie ForeignKey(SomeModel, unique=True) ?

13 votes

Oui : "Un champ OneToOneField est essentiellement le même qu'un ForeignKey, à l'exception du fait qu'il est toujours accompagné d'une contrainte "unique" et que la relation inverse renvoie toujours l'objet pointé (puisqu'il n'y en aura jamais qu'un seul), plutôt que de renvoyer une liste".

2 votes

Qu'en est-il de plusieurs voitures ayant le même moteur ?

64voto

orthodoxpirate Points 1514

Le meilleur moyen et le plus efficace d'apprendre de nouvelles choses est de voir et d'étudier des exemples pratiques du monde réel. Supposons un instant que vous vouliez construire un blog en django où les journalistes peuvent écrire et publier des articles d'actualité. Le propriétaire du journal en ligne veut permettre à chacun de ses journalistes de publier autant d'articles qu'il le souhaite, mais ne veut pas que différents journalistes travaillent sur le même article. Cela signifie que lorsque les lecteurs vont lire un article, ils ne verront qu'un seul auteur dans l'article.

Par exemple : Article par John, Article par Harry, Article par Rick. Vous ne pouvez pas avoir un article par Harry et Rick parce que le patron ne veut pas que deux auteurs ou plus travaillent sur le même article.

Comment pouvons-nous résoudre ce "problème" avec l'aide de django ? La clé de la solution de ce problème est le django. ForeignKey .

Ce qui suit est le code complet qui peut être utilisé pour mettre en œuvre l'idée de notre patron.

from django.db import models

# Create your models here.

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)

    def __unicode__(self):
        return self.first_name

class Article(models.Model):
    title = models.CharField(max_length=100)
    reporter = models.ForeignKey(Reporter)

    def __unicode__(self):
        return self.title

Exécuter python manage.py syncdb pour exécuter le code SQL et créer les tables pour votre application dans votre base de données. Utilisez ensuite python manage.py shell pour ouvrir un shell python.

Créer l'objet Reporter R1.

In [49]: from thepub.models import Reporter, Article

In [50]: R1 = Reporter(first_name='Rick')

In [51]: R1.save()

Créez l'objet Article A1.

In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1)

In [6]: A1.save()

Utilisez ensuite le code suivant pour obtenir le nom du journaliste.

In [8]: A1.reporter.first_name
Out[8]: 'Rick'

Maintenant, créez l'objet Reporter R2 en exécutant le code python suivant.

In [9]: R2 = Reporter.objects.create(first_name='Harry')

In [10]: R2.save()

Essayez maintenant d'ajouter R2 à l'objet Article A1.

In [13]: A1.reporter.add(R2)

Cela ne fonctionne pas et vous obtiendrez un AttributeError indiquant que l'objet 'Reporter' n'a pas d'attribut 'add'.

Comme vous pouvez le constater, un objet Article ne peut être lié à plus d'un objet Rapporteur.

Qu'en est-il de R1 ? Peut-on y attacher plusieurs objets Article ?

In [14]: A2 = Article.objects.create(title='Python News', reporter=R1)

In [15]: R1.article_set.all()
Out[15]: [<Article: Python News>, <Article: TDD In Django>]

Cet exemple pratique nous montre que django ForeignKey est utilisé pour définir les relations entre plusieurs personnes.

OneToOneField est utilisé pour créer des relations de type "un à un".

Nous pouvons utiliser reporter = models.OneToOneField(Reporter) dans le fichier models.py ci-dessus mais elle ne sera pas utile dans notre exemple car un auteur ne pourra pas publier plus d'un article.

Chaque fois que vous souhaitez publier un nouvel article, vous devez créer un nouvel objet Reporter. Cela prend du temps, n'est-ce pas ?

Je vous recommande vivement d'essayer l'exemple avec le OneToOneField et de réaliser la différence. Je suis presque sûr qu'après cet exemple, vous connaîtrez parfaitement la différence entre django et les autres logiciels. OneToOneField et django ForeignKey .

16voto

andrers52 Points 176

OneToOneField (un à un) réalise, dans l'orientation objet, la notion de composition, tandis que ForeignKey (un à plusieurs) concerne l'agrégation.

3 votes

Belle analogie, mais ce n'est pas toujours le cas. Il existe des cas limites qui ne correspondent pas à cette explication. Disons par exemple que nous avons des classes Patient y Organ . Patient peut avoir de nombreux Organ mais un Organ ne peut appartenir qu'à un seul Patient . Quand Patient est supprimé, tous les Organ sont également supprimés. Ils ne peuvent pas exister sans un Patient .

8voto

user1353510 Points 46

Aussi OneToOneField est utile pour être utilisé comme clé primaire afin d'éviter la duplication des clés. On peut ne pas avoir de champ automatique implicite/explicite.

models.AutoField(primary_key=True)

mais utiliser OneToOneField comme clé primaire à la place (imaginez UserProfile par exemple) :

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')

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