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
z unique=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
.
Przykład
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>]
Kod modelu
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
zeunique=True
zamiastOneToOneField
? Widzę w innych pytaniach, że Django ostrzega nawet, żeOneToOneField
najlepiej służy własnym interesom. RewersQuerySet
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.
źródło
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.
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.
Utwórz obiekt artykułu A1.
Następnie użyj następującego fragmentu kodu, aby uzyskać nazwę reportera.
Teraz utwórz obiekt Reporter R2, uruchamiając następujący kod python.
Teraz spróbuj dodać R2 do obiektu artykułu A1.
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ł?
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 djangoOneToOneField
i djangoForeignKey
.źródło
OneToOneField (jeden do jednego) realizuje, w orientacji obiektowej, pojęcie kompozycji, podczas gdy ForeignKey (jeden do wielu) odnosi się do agregacji.
źródło
Patient
iOrgan
.Patient
może mieć wieleOrgan
s, aleOrgan
może należeć tylko do jednegoPatient
. PoPatient
usunięciu usuwaneOrgan
są również wszystkie litery s. Nie mogą istnieć bezPatient
.Także
OneToOneField
jest przydatny do stosowania jako klucz podstawowy, aby uniknąć powielania klucza. Można nie mieć niejawnego / jawnego pola automatycznegoale
OneToOneField
zamiast tego użyj jako klucza podstawowego (UserProfile
na przykład wyobraź sobie model):źródło
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:
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):
W przypadku pól ForeignKey zapytania działają również w drugą stronę, ale są nieco inne ze względu na niesymetryczny charakter relacji.
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.
źródło
OneToOneField: jeśli druga tabela jest powiązana z
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
tabela 2 może zawierać więcej niż jeden rekord odpowiadający wartości pk tabeli 1.
źródło
ForeignKey pozwala na otrzymywanie podklas, czy jest to definicja innej klasy, ale OneToOneFields nie może tego zrobić i nie można go przypisać do wielu zmiennych
źródło