Risposte:
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
ForeignKey
conunique=True
, ma il lato "inverso" della relazione restituirà direttamente un singolo oggetto.
Contrariamente alla OneToOneField
relazione "inversa", una relazione ForeignKey
"inversa" restituisce a QuerySet
.
Ad esempio, se abbiamo i seguenti due modelli (codice modello completo di seguito):
Car
usi del modello OneToOneField(Engine)
Car2
usi del modello ForeignKey(Engine2, unique=True)
Dall'interno python manage.py shell
eseguire 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>
ForeignKey
con unique=True
esempio>>> from testapp.models import Car2, Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all()
[<Car2: Mazda>]
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
e.car
funziona anche?
ForeignKey
con unique=True
piuttosto che un OneToOneField
? Vedo in altre domande che Django avverte persino che gli interessi di uno di OneToOneField
solito servono al meglio. Il contrario QuerySet
non avrà mai più di un elemento, giusto?
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.
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 syncdb
per eseguire il codice sql e creare le tabelle per l'app nel database. Quindi utilizzare python manage.py shell
per 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 OneToOneField
e realizzare la differenza. Sono abbastanza sicuro che dopo questo esempio conoscerai completamente la differenza tra Django OneToOneField
e Django ForeignKey
.
OneToOneField (one-to-one) realizza, nell'orientamento agli oggetti, la nozione di composizione, mentre ForeignKey (one-to-many) si riferisce all'agregazione.
Patient
e Organ
. Patient
può avere molti Organ
s, ma Organ
può appartenere a uno solo Patient
. Quando Patient
viene eliminato, Organ
vengono eliminati anche tutti i messaggi di posta elettronica. Non possono esistere senza a Patient
.
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 OneToOneField
come chiave primaria (immagina un UserProfile
modello per esempio):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
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.
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.