Odpowiedzi:
Uważaj, aby zrozumieć, że istnieją pewne różnice między OneToOneField(SomeModel)
i ForeignKey(SomeModel, unique=True)
. Jak stwierdzono w definitywnym przewodniku po Django :
OneToOneField
Relacja jeden do jednego. Koncepcyjnie jest to podobne do
ForeignKey
zunique=True
, ale „odwrotna” strona relacji zwróci bezpośrednio pojedynczy obiekt.
W przeciwieństwie do OneToOneField
relacji ForeignKey
„odwrotnej”, relacja „odwrotna” zwraca wartość a QuerySet
.
Na przykład, jeśli mamy następujące dwa modele (pełny kod modelu poniżej):
Car
zastosowania modelu OneToOneField(Engine)
Car2
zastosowania modelu ForeignKey(Engine2, unique=True)
Od wewnątrz python manage.py shell
wykonaj następujące czynności:
OneToOneField
Przykład>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car
<Car: Audi>
ForeignKey
z unique=True
przykładem>>> 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
również działa?
ForeignKey
ze unique=True
zamiast OneToOneField
? Widzę w innych pytaniach, że Django ostrzega nawet, że OneToOneField
najlepiej służy własnym interesom. Rewers QuerySet
nigdy nie będzie miał więcej niż jednego elementu, prawda?
Klucz obcy jest przeznaczony dla jednego do wielu, więc obiekt samochodowy może mieć wiele kół, każde koło ma obcy klucz do samochodu, do którego należy. OneToOneField byłby jak silnik, w którym obiekt samochodowy może mieć jeden i tylko jeden.
Najlepszym i najskuteczniejszym sposobem uczenia się nowych rzeczy jest zapoznanie się z praktycznymi przykładami z prawdziwego świata. Załóżmy przez chwilę, że chcesz zbudować blog w django, w którym reporterzy mogą pisać i publikować artykuły. Właściciel gazety online chce, aby każdy jego reporter opublikował tyle artykułów, ile chce, ale nie chce, aby różni reporterzy pracowali nad tym samym artykułem. Oznacza to, że gdy czytelnicy pójdą i przeczytają artykuł, zobaczą tylko jednego autora w tym artykule.
Na przykład: artykuł Johna, artykuł Harry'ego, artykuł Ricka. Nie możesz mieć artykułu autorstwa Harry'ego i Ricka, ponieważ szef nie chce, aby dwóch lub więcej autorów pracowało nad tym samym artykułem.
Jak możemy rozwiązać ten „problem” przy pomocy django? Kluczem do rozwiązania tego problemu jest django ForeignKey
.
Poniżej znajduje się pełny kod, który można wykorzystać do realizacji pomysłu naszego szefa.
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
Uruchom, python manage.py syncdb
aby wykonać kod SQL i zbuduj tabele aplikacji w bazie danych. Następnie użyj, python manage.py shell
aby otworzyć powłokę pytona.
Utwórz obiekt Reporter R1.
In [49]: from thepub.models import Reporter, Article
In [50]: R1 = Reporter(first_name='Rick')
In [51]: R1.save()
Utwórz obiekt artykułu A1.
In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1)
In [6]: A1.save()
Następnie użyj następującego fragmentu kodu, aby uzyskać nazwę reportera.
In [8]: A1.reporter.first_name
Out[8]: 'Rick'
Teraz utwórz obiekt Reporter R2, uruchamiając następujący kod python.
In [9]: R2 = Reporter.objects.create(first_name='Harry')
In [10]: R2.save()
Teraz spróbuj dodać R2 do obiektu artykułu A1.
In [13]: A1.reporter.add(R2)
To nie działa i pojawi się błąd AttributeError mówiący, że obiekt „Reporter” nie ma atrybutu „dodaj”.
Jak widać obiekt Article nie może być powiązany z więcej niż jednym obiektem Reporter.
Co z R1? Czy możemy dołączyć do niego więcej niż jeden artykuł?
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>]
Ten praktyczny przykład pokazuje nam, że django ForeignKey
służy do definiowania relacji wiele do jednego.
OneToOneField
służy do tworzenia relacji jeden do jednego.
Możemy użyć reporter = models.OneToOneField(Reporter)
w powyższym pliku models.py, ale nie będzie to przydatne w naszym przykładzie, ponieważ autor nie będzie mógł opublikować więcej niż jednego artykułu.
Za każdym razem, gdy chcesz opublikować nowy artykuł, musisz utworzyć nowy obiekt Reporter. To jest czasochłonne, prawda?
Bardzo polecam wypróbować przykład z OneToOneField
i zdać sobie sprawę z różnicy. Jestem pewien, że po tym przykładzie w pełni poznasz różnicę między django OneToOneField
i django ForeignKey
.
OneToOneField (jeden do jednego) realizuje, w orientacji obiektowej, pojęcie kompozycji, podczas gdy ForeignKey (jeden do wielu) odnosi się do agregacji.
Patient
i Organ
. Patient
może mieć wiele Organ
s, ale Organ
może należeć tylko do jednego Patient
. Po Patient
usunięciu usuwane Organ
są również wszystkie litery s. Nie mogą istnieć bez Patient
.
Także OneToOneField
jest przydatny do stosowania jako klucz podstawowy, aby uniknąć powielania klucza. Można nie mieć niejawnego / jawnego pola automatycznego
models.AutoField(primary_key=True)
ale OneToOneField
zamiast tego użyj jako klucza podstawowego ( UserProfile
na przykład wyobraź sobie model):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
Gdy uzyskujesz dostęp do OneToOneField, dostajesz wartość pola, o które pytałeś. W tym przykładzie pole „tytuł” modelu książki to OneToOneField:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
Gdy uzyskujesz dostęp do klucza obcego, otrzymujesz powiązany obiekt modelu, który możesz następnie wykonać dodatkowe zapytania. W tym przykładzie pole „wydawca” tego samego modelu książki to ForeignKey (korelujący z definicją modelu klasy Publisher):
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
W przypadku pól ForeignKey zapytania działają również w drugą stronę, ale są nieco inne ze względu na niesymetryczny charakter relacji.
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
Za kulisami book_set jest tylko zestawem QuerySet i może być filtrowane i krojone jak każdy inny zestaw QuerySet. Nazwa atrybutu book_set jest generowana przez dodanie nazwy modelu małej litery do _set.
OneToOneField: jeśli druga tabela jest powiązana z
table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id')
tabela2 będzie zawierała tylko jeden rekord odpowiadający wartości pk tabeli 1, tj. tabela2_col1 będzie miała unikalną wartość równą pk tabeli
table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id')
tabela 2 może zawierać więcej niż jeden rekord odpowiadający wartości pk tabeli 1.