web-dev-qa-db-fra.com

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

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

340
redice

Veillez à comprendre qu'il existe des différences entre OneToOneField(SomeModel) et ForeignKey(SomeModel, unique=True). Comme indiqué dans Le Guide définitif de Django :

OneToOneField

Une relation un à un. Conceptuellement, cela ressemble à un ForeignKey avec unique=True, mais le côté "inversé" de la relation retournera directement un seul objet.

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

Exemple

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

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

Depuis python manage.py Shell exécutez les opérations suivantes:

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 avec 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 de 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
440
Matthew Rankin

Une ForeignKey est pour un à plusieurs, donc un objet Car peut avoir plusieurs roues, chaque roue ayant une ForeignKey vers la voiture à laquelle il appartient. Un OneToOneField ressemblerait à un moteur, un objet Car pouvant en avoir un et un seul.

99
Dan Breen

Le moyen le plus efficace d’apprendre de nouvelles choses est de voir et d’étudier des exemples concrets du monde réel. Supposons un instant que vous souhaitiez créer un blog dans Django où les journalistes peuvent rédiger et publier des articles de presse. Le propriétaire du journal en ligne souhaite permettre à chacun de ses journalistes de publier autant d'articles qu'ils le souhaitent, mais ne souhaite pas que différents journalistes travaillent sur le même article. Cela signifie que lorsque les lecteurs lisent un article, ils ne sont représentés que par un seul auteur.

Par exemple: Article de John, Article de Harry, Article de Rick. Vous ne pouvez pas avoir Article de Harry & Rick car le patron ne veut pas que deux ou plusieurs auteurs 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 implémenter 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écutez python manage.py syncdb pour exécuter le code SQL et créer les tables de votre application dans votre base de données. Ensuite, utilisez python manage.py Shell pour ouvrir un shell python.

Créez 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()

Ensuite, utilisez le code suivant pour obtenir le nom du journaliste.

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

Créez maintenant l'objet Reporter R2 en exécutant le code python suivant.

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

In [10]: R2.save()

Maintenant, essayez d'ajouter R2 à l'objet Article A1.

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

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

Comme vous pouvez le constater, un objet Article ne peut pas être associé à plus d’un objet Reporter.

Qu'en est-il de R1? Peut-on y attacher plus d’un objet?

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 des relations plusieurs-à-un.

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

Nous pouvons utiliser reporter = models.OneToOneField(Reporter) dans le fichier models.py ci-dessus, mais cela 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 recommande fortement 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 saurez parfaitement la différence entre Django OneToOneField et Django ForeignKey.

32
jetbird13

OneToOneField (one-to-one) réalise, en orientation objet, la notion de composition, alors que ForeignKey (un-plusieurs) se rapporte à l'agrégation.

11
andrers52

Il est également utile d'utiliser OneToOneField comme clé primaire pour éviter la duplication de clé. On peut ne pas avoir autofield implicite/explicite

models.AutoField(primary_key=True)

mais utilisez plutôt OneToOneField comme clé primaire (imaginez le modèle UserProfile par exemple):

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

Lorsque vous accédez à OneToOneField, vous obtenez la valeur du champ que vous avez interrogé. Dans cet exemple, le champ 'titre' d'un modèle de livre est un OneToOneField:

>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'

Lorsque vous accédez à une clé étrangère, vous obtenez l'objet de modèle associé, sur lequel vous pouvez ensuite effectuer d'autres requêtes. Dans cet exemple, le champ "éditeur" du même modèle de livre est une ForeignKey (en corrélation avec la définition du modèle de classe Publisher):

>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'

Avec ForeignKey, les requêtes fonctionnent également dans l'autre sens, mais elles sont légèrement différentes en raison de la nature non symétrique de la relation.

>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]

Dans les coulisses, book_set n'est qu'un QuerySet et peut être filtré et découpé en tranches comme n'importe quel autre QuerySet. Le nom d'attribut book_set est généré en ajoutant le nom de modèle en minuscule à _set.

2
Yup.

OneToOneField: si la deuxième table est liée à

table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id')

table2 contiendra un seul enregistrement correspondant à la valeur pk de table1, c'est-à-dire que table2_col1 aura une valeur unique égale à pk de la table

table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id')

table2 peut contenir plus d'un enregistrement correspondant à la valeur pk de table1.

0
aarif faridi