Apa perbedaan antara django OneToOneField dan ForeignKey?

402

Apa perbedaan antara Django OneToOneFielddan ForeignKey?

redice
sumber

Jawaban:

507

Berhati-hatilah untuk menyadari bahwa ada beberapa perbedaan antara OneToOneField(SomeModel)dan ForeignKey(SomeModel, unique=True). Sebagaimana dinyatakan dalam The Definitive Guide to Django :

OneToOneField

Hubungan satu-ke-satu. Secara konseptual, ini mirip dengan ForeignKeydengan unique=True, tetapi sisi "terbalik" dari relasi akan langsung mengembalikan satu objek.

Berbeda dengan OneToOneFieldrelasi "mundur", relasi ForeignKey"terbalik" mengembalikan a QuerySet.

Contoh

Misalnya, jika kita memiliki dua model berikut (kode model lengkap di bawah):

  1. Car menggunakan model OneToOneField(Engine)
  2. Car2 menggunakan model ForeignKey(Engine2, unique=True)

Dari dalam python manage.py shelljalankan yang berikut:

OneToOneField Contoh

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

ForeignKeydengan unique=TrueContoh

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

Kode model

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
sumber
5
@ MarkPeyer: Sejauh yang saya mengerti, bidang OneToOne hanya itu: satu-ke-satu. Tidak harus ke atas. Lihat contoh ini : tempat tidak harus menjadi restoran.
Osa
21
Jawaban ini mengatakan "ada beberapa perbedaan", dan kemudian menyebutkan satu perbedaan. Apakah ada yang lain?
Chris Martin
6
Saya bertanya-tanya sama dengan Chris. Apakah ini hanya gula sintaksis, apakah ada beberapa perbedaan mendasar dalam cara data diakses, yang mengarah ke perbedaan kinerja?
Carlos
4
Apakah ada alasan mendasar mengapa Django tidak dapat memiliki aturan sedemikian rupa sehingga jika kunci asing itu unik dan bukan nol, maka e.carjuga berfungsi?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
Jadi ... kapan seseorang bahkan ingin menggunakan ForeignKeydengan unique=Truebukan OneToOneField? Saya melihat dalam pertanyaan-pertanyaan lain bahwa Django bahkan memperingatkan bahwa OneToOneFieldkepentingan yang biasanya terbaik adalah kepentingan seseorang. Kebalikannya QuerySettidak akan pernah memiliki lebih dari satu elemen, kan?
Andy
121

ForeignKey adalah untuk satu-ke-banyak, jadi objek Mobil mungkin memiliki banyak Roda, setiap Roda memiliki ForeignKey untuk Mobil miliknya. OneToOneField akan seperti Engine, di mana objek Mobil dapat memiliki satu dan hanya satu.

Dan Breen
sumber
4
terima kasih, Dosis OneToOneField (someModel) berarti ForeignKey (SomeModel, unique = True)?
kembalikan
9
Ya: 'A OneToOneField pada dasarnya sama dengan ForeignKey, dengan pengecualian yang selalu membawa kendala "unik" dengannya dan relasi terbalik selalu mengembalikan objek yang ditunjuk (karena hanya akan ada satu), daripada mengembalikan sebuah daftar.'
Dan Breen
1
Bagaimana dengan beberapa mobil yang memiliki mesin yang sama?
Oleg Belousov
3
@OlegTikhonov Mereka mungkin memiliki salinan desain mesin yang sama, tapi saya ingin melihat contoh di mana beberapa mobil berbagi mesin fisik yang sama.
Dan Breen
3
Ada sedikit kebingungan tentang istilah dalam jawaban ini. ForeignKey bukan hubungan satu-ke-banyak tetapi merupakan hubungan banyak-ke-satu menurut dokumentasi Django resmi: docs.djangoproject.com/en/2.0/ref/models/fields/…
Kutay Demireren
45

Cara terbaik dan paling efektif untuk mempelajari hal-hal baru adalah dengan melihat dan mempelajari contoh-contoh praktis dunia nyata. Misalkan sesaat Anda ingin membangun blog di Django di mana wartawan dapat menulis dan menerbitkan artikel berita. Pemilik surat kabar online ingin mengizinkan masing-masing reporternya menerbitkan sebanyak mungkin artikel yang mereka inginkan, tetapi tidak ingin reporter yang berbeda mengerjakan artikel yang sama. Ini berarti bahwa ketika pembaca pergi dan membaca artikel mereka hanya akan melihat satu penulis dalam artikel tersebut.

Misalnya: Artikel oleh John, Artikel oleh Harry, Artikel oleh Rick. Anda tidak dapat memiliki Artikel oleh Harry & Rick karena bos tidak ingin dua atau lebih penulis mengerjakan artikel yang sama.

Bagaimana kita bisa memecahkan 'masalah' ini dengan bantuan Django? Kunci untuk pemecahan masalah ini adalah Django ForeignKey.

Berikut ini adalah kode lengkap yang dapat digunakan untuk menerapkan gagasan bos kami.

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

Jalankan python manage.py syncdbuntuk mengeksekusi kode sql dan membangun tabel untuk aplikasi Anda di database Anda. Kemudian gunakan python manage.py shelluntuk membuka shell python.

Buat objek Reporter R1.

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

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

In [51]: R1.save()

Buat objek Artikel A1.

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

In [6]: A1.save()

Kemudian gunakan potongan kode berikut untuk mendapatkan nama reporter.

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

Sekarang buat objek Reporter R2 dengan menjalankan kode python berikut.

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

In [10]: R2.save()

Sekarang coba tambahkan R2 ke objek Artikel A1.

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

Itu tidak bekerja dan Anda akan mendapatkan sebuah AttributeError yang mengatakan objek 'Reporter' tidak memiliki atribut 'tambah'.

Seperti yang Anda lihat objek Artikel tidak dapat dikaitkan dengan lebih dari satu objek Reporter.

Bagaimana dengan R1? Bisakah kita melampirkan lebih dari satu artikel objek ke dalamnya?

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

Contoh praktis ini menunjukkan kepada kita bahwa Django ForeignKeydigunakan untuk mendefinisikan banyak-ke-satu hubungan.

OneToOneField digunakan untuk membuat hubungan satu-ke-satu.

Kita dapat menggunakan reporter = models.OneToOneField(Reporter)dalam file models.py di atas tetapi itu tidak akan berguna dalam contoh kita sebagai penulis tidak akan dapat memposting lebih dari satu artikel.

Setiap kali Anda ingin memposting artikel baru, Anda harus membuat objek Reporter baru. Ini menghabiskan waktu, bukan?

Saya sangat merekomendasikan untuk mencoba contohnya dengan OneToOneFielddan sadari perbedaannya. Saya cukup yakin bahwa setelah contoh ini Anda akan sepenuhnya mengetahui perbedaan antara Django OneToOneFielddan Django ForeignKey.

jetbird13
sumber
Saya suka ini. Perbedaan mendasar antara OneToOne dan ForeignKey adalah hubungan satu ke satu dan satu ke banyak. Anda dapat menggunakan ForeignKey dan unique = True untuk melakukan satu-ke-satu, perbedaan halus dinyatakan dalam balasan Matius.
FrankZhu
13

OneToOneField (satu-ke-satu) menyadari, dalam orientasi objek, gagasan komposisi, sementara ForeignKey (satu-ke-banyak) berkaitan dengan persetujuan.

andrers52
sumber
3
Analogi yang bagus, tetapi tidak selalu seperti itu. Ada beberapa kasus tepi yang tidak sesuai dengan penjelasan ini. Katakanlah misalnya kita memiliki kelas Patientdan Organ. Patientdapat memiliki banyak Organs, tetapi Organkaleng hanya milik satu Patient. Ketika Patientdihapus, semua Organjuga dihapus. Mereka tidak bisa eksis tanpa a Patient.
cezar
4

Juga OneToOneFieldberguna untuk digunakan sebagai kunci utama untuk menghindari duplikasi kunci. Seseorang mungkin tidak memiliki autofield implisit / eksplisit

models.AutoField(primary_key=True)

tetapi gunakan OneToOneFieldsebagai kunci utama sebagai gantinya (bayangkan UserProfilemodel misalnya):

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

Ketika Anda mengakses OneToOneField Anda mendapatkan nilai dari bidang yang Anda tanyakan. Dalam contoh ini, bidang 'judul' model buku adalah OneToOneField:

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

Saat Anda mengakses ForeignKey, Anda mendapatkan objek model terkait, yang kemudian dapat Anda buat sebelumnya. Dalam contoh ini, bidang 'penerbit' model buku yang sama adalah ForeignKey (berkorelasi dengan definisi model kelas Penerbit):

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

Dengan bidang ForeignKey, kueri juga berfungsi sebaliknya, tetapi keduanya sedikit berbeda karena sifat hubungan yang tidak simetris.

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

Di belakang layar, book_set hanyalah QuerySet dan dapat difilter dan diiris seperti QuerySet lainnya. Nama atribut book_set dihasilkan dengan menambahkan nama model huruf kecil ke _set.

Ya.
sumber
1

OneToOneField: jika tabel kedua terkait dengan

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

table2 hanya akan berisi satu record yang sesuai dengan nilai pk table1, yaitu table2_col1 akan memiliki nilai unik sama dengan pk tabel

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

table2 dapat berisi lebih dari satu record yang sesuai dengan nilai pk table1.

aarif faridi
sumber
1

ForeignKey memungkinkan Anda menerima subclass apakah itu definisi kelas lain tetapi OneToOneFields tidak dapat melakukan ini dan tidak dapat dilampirkan ke beberapa variabel

Bitdom8
sumber