Jaka jest różnica między django OneToOneField a ForeignKey?

402

Jaka jest różnica między Django OneToOneFielda ForeignKey?

redice
źródło

Odpowiedzi:

507

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 ForeignKeyz unique=True, ale „odwrotna” strona relacji zwróci bezpośrednio pojedynczy obiekt.

W przeciwieństwie do OneToOneFieldrelacji 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):

  1. Car zastosowania modelu OneToOneField(Engine)
  2. Car2 zastosowania modelu ForeignKey(Engine2, unique=True)

Od wewnątrz python manage.py shellwykonaj 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>

ForeignKeyz unique=Trueprzykł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
Matthew Rankin
źródło
5
@MarkPNeyer: o ile rozumiem, pole OneToOne jest po prostu takie: jeden do jednego. Nie musi być włączony. Zobacz ten przykład : miejsce nie musi być restauracją.
osa
21
Ta odpowiedź mówi „istnieją pewne różnice”, a następnie wymienia jedną różnicę. Czy są jeszcze inni?
Chris Martin,
6
Zastanawiam się tak samo jak Chris. Czy to po prostu cukier składniowy, czy istnieje jakaś podstawowa różnica w sposobie dostępu do danych, prowadząca do różnic w wydajności?
Carlos
4
Czy istnieje fundamentalny powód, dla którego Django nie mógł mieć takiej reguły, że jeśli klucz obcy jest unikalny i nie ma wartości zerowej, to e.carrównież działa?
Ciro Santilli 10 冠状 病 六四 事件 法轮功
4
Więc ... kiedy jeden jeszcze chcesz użyć ForeignKeyze unique=Truezamiast OneToOneField? Widzę w innych pytaniach, że Django ostrzega nawet, że OneToOneFieldnajlepiej służy własnym interesom. Rewers QuerySetnigdy nie będzie miał więcej niż jednego elementu, prawda?
Andy
121

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.

Dan Breen
źródło
4
dziękuję, Dawka OneToOneField (someModel) oznacza ForeignKey (SomeModel, unique = True)?
redice
9
Tak: „OneToOneField jest zasadniczo taki sam jak ForeignKey, z wyjątkiem tego, że zawsze ma z nim„ unikalne ”ograniczenie, a odwrotna relacja zawsze zwraca wskazany obiekt (ponieważ zawsze będzie tylko jeden), zamiast zwracać lista.'
Dan Breen
1
Co powiesz na kilka samochodów z tym samym silnikiem?
Oleg Belousov,
3
@OlegTikhonov Mogą mieć kopię tego samego projektu silnika, ale chciałbym zobaczyć przypadek, w którym kilka samochodów korzysta z tego samego silnika fizycznego.
Dan Breen,
3
Jest trochę zamieszania w odniesieniu do warunków w tej odpowiedzi. ForeignKey nie jest relacją jeden do wielu, ale zgodnie z oficjalną dokumentacją django jest to relacja jeden do wielu: docs.djangoproject.com/en/2.0/ref/models/fields/…
Demireren
45

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 syncdbaby wykonać kod SQL i zbuduj tabele aplikacji w bazie danych. Następnie użyj, python manage.py shellaby 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 ForeignKeysł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 OneToOneFieldi zdać sobie sprawę z różnicy. Jestem pewien, że po tym przykładzie w pełni poznasz różnicę między django OneToOneFieldi django ForeignKey.

jetbird13
źródło
Lubię to. Podstawowa różnica między OneToOne a ForeignKey polega na relacji jeden do jednego i jeden do wielu. Możesz użyć ForeignKey i unique = True, aby zrobić jeden na jednego, subtelna różnica jest podana w odpowiedzi Matthew.
FrankZhu,
13

OneToOneField (jeden do jednego) realizuje, w orientacji obiektowej, pojęcie kompozycji, podczas gdy ForeignKey (jeden do wielu) odnosi się do agregacji.

andrers52
źródło
3
Fajna analogia, ale nie zawsze tak jest. Istnieje kilka przypadków skrajnych, które nie pasują do tego wyjaśnienia. Powiedzmy na przykład, że mamy zajęcia Patienti Organ. Patientmoże mieć wiele Organs, ale Organmoże należeć tylko do jednego Patient. Po Patientusunięciu usuwane Organsą również wszystkie litery s. Nie mogą istnieć bez Patient.
cezar
4

Także OneToOneFieldjest przydatny do stosowania jako klucz podstawowy, aby uniknąć powielania klucza. Można nie mieć niejawnego / jawnego pola automatycznego

models.AutoField(primary_key=True)

ale OneToOneFieldzamiast tego użyj jako klucza podstawowego ( UserProfilena przykład wyobraź sobie model):

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')
Dmitrij Sintsov
źródło
3

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.

Tak.
źródło
1

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.

aarif faridi
źródło
1

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

Bitdom8
źródło