Qual è la differenza tra django OneToOneField e ForeignKey?


Risposte:


507

Fai attenzione a capire che ci sono alcune differenze tra OneToOneField(SomeModel)e ForeignKey(SomeModel, unique=True). Come affermato nella Guida definitiva a Django :

OneToOneField

Una relazione uno a uno. Concettualmente, questo è simile a ForeignKeycon unique=True, ma il lato "inverso" della relazione restituirà direttamente un singolo oggetto.

Contrariamente alla OneToOneFieldrelazione "inversa", una relazione ForeignKey"inversa" restituisce a QuerySet.

Esempio

Ad esempio, se abbiamo i seguenti due modelli (codice modello completo di seguito):

  1. Car usi del modello OneToOneField(Engine)
  2. Car2 usi del modello ForeignKey(Engine2, unique=True)

Dall'interno python manage.py shelleseguire quanto segue:

OneToOneField Esempio

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

ForeignKeycon unique=Trueesempio

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

Codice modello

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
@MarkPNeyer: per quanto ho capito, un campo OneToOne è proprio quello: uno a uno. Non deve essere su. Vedi questo esempio : un luogo non deve essere un ristorante.
osa,

21
Questa risposta dice "ci sono alcune differenze" e quindi nomina una differenza. Ce ne sono altri?
Chris Martin,

6
Mi chiedo lo stesso di Chris. È semplicemente zucchero sintattico, c'è qualche differenza di fondo nel modo in cui si accede ai dati, portando a differenze di prestazioni?
Carlos,

4
C'è una ragione fondamentale per cui Django non potrebbe avere una regola tale che se la chiave esterna è unica e non nulla, allora e.carfunziona anche?
Ciro Santilli 10 冠状 病 六四 事件 法轮功

4
Quindi ... quando uno vorrebbe persino usare un ForeignKeycon unique=Truepiuttosto che un OneToOneField? Vedo in altre domande che Django avverte persino che gli interessi di uno di OneToOneFieldsolito servono al meglio. Il contrario QuerySetnon avrà mai più di un elemento, giusto?
Andy,

121

Un ForeignKey è per uno-a-molti, quindi un oggetto Car potrebbe avere molte Ruote, ciascuna delle quali ha un ForeignKey sull'auto a cui appartiene. Un OneToOneField sarebbe come un motore, in cui un oggetto Car può avere uno e solo uno.


4
grazie, Dose OneToOneField (someModel) significa ForeignKey (SomeModel, unique = True)?
Redice

9
Sì: 'Un OneToOneField è essenzialmente lo stesso di un ForeignKey, con l'eccezione che porta sempre un vincolo "unico" con esso e la relazione inversa restituisce sempre l'oggetto puntato (dal momento che ce ne sarà solo uno), piuttosto che restituire un elenco.'
Dan Breen,

1
Che dire di diverse auto che hanno lo stesso motore?
Oleg Belousov,

3
@OlegTikhonov Potrebbero avere una copia dello stesso design del motore, ma mi piacerebbe vedere un'istanza in cui diverse auto condividono lo stesso motore fisico.
Dan Breen,

3
C'è un po 'di confusione sui termini di questa risposta. ForeignKey non è uno a molti ma è una relazione molti a uno secondo la documentazione ufficiale di django: docs.djangoproject.com/en/2.0/ref/models/fields/…
Kutay Demireren

45

Il modo migliore e più efficace per imparare cose nuove è vedere e studiare esempi pratici del mondo reale. Supponiamo per un momento di voler creare un blog in Django in cui i giornalisti possano scrivere e pubblicare articoli di notizie. Il proprietario del giornale online desidera consentire a ciascuno dei suoi giornalisti di pubblicare tutti gli articoli che desidera, ma non desidera che diversi giornalisti lavorino sullo stesso articolo. Ciò significa che quando i lettori vanno a leggere un articolo vedranno un solo autore nell'articolo.

Ad esempio: articolo di John, articolo di Harry, articolo di Rick. Non puoi avere l'articolo di Harry & Rick perché il capo non vuole che due o più autori lavorino sullo stesso articolo.

Come possiamo risolvere questo "problema" con l'aiuto di Django? La chiave per la soluzione di questo problema è il django ForeignKey.

Di seguito è riportato il codice completo che può essere utilizzato per implementare l'idea del nostro capo.

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

Esegui python manage.py syncdbper eseguire il codice sql e creare le tabelle per l'app nel database. Quindi utilizzare python manage.py shellper aprire una shell Python.

Creare l'oggetto Reporter R1.

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

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

In [51]: R1.save()

Creare l'oggetto articolo A1.

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

In [6]: A1.save()

Quindi utilizzare il seguente codice per ottenere il nome del reporter.

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

Ora crea l'oggetto Reporter R2 eseguendo il seguente codice Python.

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

In [10]: R2.save()

Ora prova ad aggiungere R2 all'oggetto Articolo A1.

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

Non funziona e otterrai un AttributeError che dice che l'oggetto 'Reporter' non ha alcun attributo 'add'.

Come puoi vedere, un oggetto Article non può essere correlato a più di un oggetto Reporter.

Che mi dici di R1? Possiamo allegare più di un oggetto Articolo ad esso?

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

Questo esempio pratico ci mostra che django ForeignKeyè usato per definire relazioni molti-a-uno.

OneToOneField viene utilizzato per creare relazioni uno a uno.

Possiamo usare reporter = models.OneToOneField(Reporter)il file models.py sopra ma non sarà utile nel nostro esempio poiché un autore non sarà in grado di pubblicare più di un articolo.

Ogni volta che vuoi pubblicare un nuovo articolo dovrai creare un nuovo oggetto Reporter. Questo richiede tempo, non è vero?

Consiglio vivamente di provare l'esempio con OneToOneFielde realizzare la differenza. Sono abbastanza sicuro che dopo questo esempio conoscerai completamente la differenza tra Django OneToOneFielde Django ForeignKey.


Mi piace questo. La differenza fondamentale tra OneToOne e ForeignKey è una relazione uno a uno e una a molte. Puoi usare ForeignKey e unique = True per fare uno a uno, la sottile differenza è dichiarata nella risposta di Matthew.
FrankZhu,

13

OneToOneField (one-to-one) realizza, nell'orientamento agli oggetti, la nozione di composizione, mentre ForeignKey (one-to-many) si riferisce all'agregazione.


3
Bella analogia, ma non è sempre così. Ci sono alcuni casi limite che non rientrano in questa spiegazione. Diciamo ad esempio che abbiamo lezioni Patiente Organ. Patientpuò avere molti Organs, ma Organpuò appartenere a uno solo Patient. Quando Patientviene eliminato, Organvengono eliminati anche tutti i messaggi di posta elettronica. Non possono esistere senza a Patient.
Cezar

4

Inoltre OneToOneFieldè utile essere usato come chiave primaria per evitare la duplicazione della chiave. Uno potrebbe non avere autofield implicito / esplicito

models.AutoField(primary_key=True)

ma usa invece OneToOneFieldcome chiave primaria (immagina un UserProfilemodello per esempio):

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

3

Quando si accede a OneToOneField si ottiene il valore del campo richiesto. In questo esempio, il campo "titolo" di un modello di libro è OneToOneField:

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

Quando si accede a ForeignKey, si ottiene l'oggetto modello correlato, al quale è possibile preformare ulteriori query. In questo esempio il campo "editore" dello stesso modello di libro è ForeignKey (correlato alla definizione del modello di classe Publisher):

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

Con i campi ForeignKey le query funzionano anche nell'altro modo, ma sono leggermente diverse a causa della natura non simmetrica della relazione.

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

Dietro le quinte, book_set è solo un QuerySet e può essere filtrato e suddiviso come qualsiasi altro QuerySet. Il nome dell'attributo book_set viene generato aggiungendo il nome del modello in minuscolo a _set.


1

OneToOneField: se la seconda tabella è correlata

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

table2 conterrà solo un record corrispondente al valore pk di table1, ovvero table2_col1 avrà un valore univoco pari a pk della tabella

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

table2 può contenere più di un record corrispondente al valore pk di table1.


1

ForeignKey ti consente di ricevere sottoclassi se è la definizione di un'altra classe ma OneToOneFields non può farlo e non è collegabile a più variabili

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.