Was ist der Unterschied zwischen django OneToOneField und ForeignKey?

402

Was ist der Unterschied zwischen Django OneToOneFieldund ForeignKey?

redice
quelle

Antworten:

507

Achten Sie darauf, dass es einige Unterschiede zwischen OneToOneField(SomeModel)und gibt ForeignKey(SomeModel, unique=True). Wie im endgültigen Leitfaden für Django angegeben :

OneToOneField

Eine Eins-zu-Eins-Beziehung. Konzeptionell ähnelt dies einem ForeignKeywith unique=True, aber die "umgekehrte" Seite der Beziehung gibt direkt ein einzelnes Objekt zurück.

Im Gegensatz zur OneToOneField"umgekehrten" Beziehung gibt eine ForeignKey"umgekehrte" Beziehung a zurück QuerySet.

Beispiel

Zum Beispiel, wenn wir die folgenden zwei Modelle haben (vollständiger Modellcode unten):

  1. Car Modell verwendet OneToOneField(Engine)
  2. Car2 Modell verwendet ForeignKey(Engine2, unique=True)

python manage.py shellFühren Sie von innen Folgendes aus:

OneToOneField Beispiel

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

ForeignKeymit unique=TrueBeispiel

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

Modellnummer

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
Matthew Rankin
quelle
5
@ MarkPNeyer: Soweit ich weiß, ist ein OneToOne-Feld genau das: eins zu eins. Es muss nicht auf sein. Siehe dieses Beispiel : Ein Ort muss kein Restaurant sein.
osa
21
Diese Antwort sagt "es gibt einige Unterschiede" und nennt dann einen Unterschied. Gibt es noch andere
Chris Martin
6
Ich frage mich genauso wie Chris. Ist es einfach syntaktischer Zucker, gibt es einen grundlegenden Unterschied beim Zugriff auf die Daten, der zu Leistungsunterschieden führt?
Carlos
4
Gibt es einen fundamentalen Grund, warum Django keine Regel haben konnte, die e.carfunktioniert , wenn der Fremdschlüssel eindeutig und nicht null ist ?
Ciro Santilli 10 冠状 病 六四 事件 10
4
Also ... wann würde man überhaupt ein ForeignKeymit unique=Trueanstatt eines verwenden wollen OneToOneField? Ich sehe in anderen Fragen, dass Django sogar warnt, dass OneToOneFielddie Interessen eines normalerweise am besten dienen. Das Gegenteil QuerySetwird niemals mehr als ein Element haben, oder?
Andy
121

Ein ForeignKey ist für eins zu viele, daher kann ein Autoobjekt viele Räder haben, wobei jedes Rad einen ForeignKey für das Auto hat, zu dem es gehört. Ein OneToOneField wäre wie ein Motor, bei dem ein Autoobjekt nur einen haben kann.

Dan Breen
quelle
4
Danke, Dose OneToOneField (someModel) bedeutet ForeignKey (SomeModel, unique = True)?
redice
9
Ja: 'Ein OneToOneField ist im Wesentlichen dasselbe wie ein ForeignKey, mit der Ausnahme, dass immer eine "eindeutige" Einschränkung vorliegt und die umgekehrte Beziehung immer das Objekt zurückgibt, auf das verwiesen wird (da es immer nur eines geben wird), anstatt a zurückzugeben Liste.'
Dan Breen
1
Was ist mit mehreren Autos mit demselben Motor?
Oleg Belousov
3
@OlegTikhonov Sie haben möglicherweise eine Kopie des gleichen Motordesigns , aber ich möchte eine Instanz sehen, in der mehrere Autos denselben physischen Motor teilen.
Dan Breen
3
Es gibt ein wenig Verwirrung über die Begriffe in dieser Antwort. ForeignKey ist keine Eins-zu-Viele-Beziehung, aber es ist eine Viele-zu-Eins-Beziehung gemäß der offiziellen Django-Dokumentation: docs.djangoproject.com/de/2.0/ref/models/fields/…
Kutay Demireren
45

Der beste und effektivste Weg, neue Dinge zu lernen, besteht darin, praktische Beispiele aus der Praxis zu sehen und zu studieren. Angenommen, Sie möchten für einen Moment ein Blog in Django erstellen, in dem Reporter Nachrichtenartikel schreiben und veröffentlichen können. Der Inhaber der Online-Zeitung möchte jedem seiner Reporter erlauben, so viele Artikel zu veröffentlichen, wie er möchte, möchte jedoch nicht, dass verschiedene Reporter an demselben Artikel arbeiten. Dies bedeutet, dass Leser, die einen Artikel lesen, nur einen Autor im Artikel sehen.

Zum Beispiel: Artikel von John, Artikel von Harry, Artikel von Rick. Sie können keinen Artikel von Harry & Rick haben, da der Chef nicht möchte, dass zwei oder mehr Autoren an demselben Artikel arbeiten.

Wie können wir dieses "Problem" mit Hilfe von Django lösen? Der Schlüssel zur Lösung dieses Problems ist der Django ForeignKey.

Das Folgende ist der vollständige Code, mit dem die Idee unseres Chefs umgesetzt werden kann.

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

Führen Sie python manage.py syncdbdiese Option aus, um den SQL-Code auszuführen und die Tabellen für Ihre App in Ihrer Datenbank zu erstellen. Verwenden Sie dann python manage.py shell, um eine Python-Shell zu öffnen.

Erstellen Sie das Reporter-Objekt R1.

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

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

In [51]: R1.save()

Erstellen Sie das Artikelobjekt A1.

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

In [6]: A1.save()

Verwenden Sie dann den folgenden Code, um den Namen des Reporters abzurufen.

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

Erstellen Sie nun das Reporter-Objekt R2, indem Sie den folgenden Python-Code ausführen.

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

In [10]: R2.save()

Versuchen Sie nun, R2 zum Artikelobjekt A1 hinzuzufügen.

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

Es funktioniert nicht und Sie erhalten einen AttributeError, der besagt, dass das Objekt 'Reporter' kein Attribut 'add' hat.

Wie Sie sehen, kann ein Artikelobjekt nicht mit mehr als einem Reporterobjekt verknüpft werden.

Was ist mit R1? Können wir mehr als ein Artikelobjekt daran anhängen?

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>]

Dieses praktische Beispiel zeigt uns, dass Django ForeignKeyverwendet wird, um viele-zu-eins-Beziehungen zu definieren.

OneToOneField wird verwendet, um Eins-zu-Eins-Beziehungen zu erstellen.

Wir können reporter = models.OneToOneField(Reporter)die obige Datei models.py verwenden, sie ist jedoch in unserem Beispiel nicht hilfreich, da ein Autor nicht mehr als einen Artikel veröffentlichen kann.

Jedes Mal, wenn Sie einen neuen Artikel veröffentlichen möchten, müssen Sie ein neues Reporter-Objekt erstellen. Das ist zeitaufwändig, nicht wahr?

Ich empfehle dringend, das Beispiel mit dem zu versuchen OneToOneFieldund den Unterschied zu erkennen. Ich bin mir ziemlich sicher, dass Sie nach diesem Beispiel den Unterschied zwischen Django OneToOneFieldund Django vollständig kennen ForeignKey.

jetbird13
quelle
Ich mag das. Der grundlegende Unterschied zwischen OneToOne und ForeignKey besteht in einer Eins-zu-Eins- und einer Eins-zu-Viele-Beziehung. Sie könnten ForeignKey und unique = True verwenden, um eins zu eins zu arbeiten. Der subtile Unterschied wird in Matthews Antwort angegeben.
FrankZhu
13

OneToOneField (Eins-zu-Eins) verwirklicht in der Objektorientierung den Begriff der Komposition, während ForeignKey (Eins-zu-Viele) sich auf Agregation bezieht.

andrers52
quelle
3
Schöne Analogie, aber das ist nicht immer so. Es gibt einige Randfälle, die nicht in diese Erklärung passen. Nehmen wir zum Beispiel an, wir haben Klassen Patientund Organ. Patientkann viele Organs haben, aber eine Organkann nur zu einem gehören Patient. Wenn Patientgelöscht wird, werden auch alle Organs gelöscht. Sie können nicht ohne existieren Patient.
Cezar
4

Es OneToOneFieldist auch nützlich, als Primärschlüssel verwendet zu werden, um das Duplizieren von Schlüsseln zu vermeiden. Man kann kein implizites / explizites Autofeld haben

models.AutoField(primary_key=True)

Verwenden Sie OneToOneFieldstattdessen den Primärschlüssel (stellen Sie sich beispielsweise ein UserProfileModell vor):

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

Wenn Sie auf ein OneToOneField zugreifen, erhalten Sie den Wert des abgefragten Feldes. In diesem Beispiel ist das Titelfeld eines Buchmodells ein OneToOneField:

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

Wenn Sie auf einen ForeignKey zugreifen, erhalten Sie das zugehörige Modellobjekt, für das Sie weitere Abfragen durchführen können. In diesem Beispiel ist das Feld "Publisher" des gleichen Buchmodells ein ForeignKey (korreliert mit der Definition des Publisher-Klassenmodells):

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

Bei ForeignKey-Feldern funktionieren Abfragen auch umgekehrt, unterscheiden sich jedoch aufgrund der nicht symmetrischen Natur der Beziehung geringfügig.

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

Hinter den Kulissen ist book_set nur ein QuerySet und kann wie jedes andere QuerySet gefiltert und in Scheiben geschnitten werden. Der Attributname book_set wird generiert, indem der Modellname in Kleinbuchstaben an _set angehängt wird.

Jep.
quelle
1

OneToOneField: Wenn die zweite Tabelle mit verwandt ist

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

Tabelle2 enthält nur einen Datensatz, der dem pk-Wert von Tabelle1 entspricht, dh table2_col1 hat einen eindeutigen Wert, der pk der Tabelle entspricht

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

Tabelle2 kann mehr als einen Datensatz enthalten, der dem pk-Wert von Tabelle1 entspricht.

aarif faridi
quelle
1

Mit ForeignKey können Sie Unterklassen empfangen, wenn es sich um eine Definition einer anderen Klasse handelt. OneToOneFields kann dies jedoch nicht und kann nicht an mehrere Variablen angehängt werden

Bitdom8
quelle