django OneToOneField กับ ForeignKey ต่างกันอย่างไร


คำตอบ:


507

โปรดใช้ความระมัดระวังที่จะตระหนักว่ามีความแตกต่างบางอย่างระหว่างและOneToOneField(SomeModel) ForeignKey(SomeModel, unique=True)ตามที่ระบุไว้ในคู่มือการแตกหักเพื่อ Django :

OneToOneField

ความสัมพันธ์แบบหนึ่งต่อหนึ่ง แนวคิดนี้คล้ายForeignKeyกับด้วยunique=Trueแต่ด้าน "ย้อนกลับ" ของความสัมพันธ์จะส่งคืนวัตถุเดียวโดยตรง

ในทางตรงกันข้ามกับOneToOneField"ย้อนกลับ" ความสัมพันธ์เป็นForeignKey"ย้อนกลับ" QuerySetความสัมพันธ์ผลตอบแทน

ตัวอย่าง

ตัวอย่างเช่นหากเรามีสองรุ่นต่อไปนี้ (รหัสรุ่นเต็มด้านล่าง):

  1. Car รูปแบบการใช้งาน OneToOneField(Engine)
  2. Car2 รูปแบบการใช้งาน ForeignKey(Engine2, unique=True)

จากภายในpython manage.py shellดำเนินการดังต่อไปนี้:

OneToOneField ตัวอย่าง

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

ForeignKeyด้วยunique=Trueตัวอย่าง

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

5
@ MarkPNeyer: เท่าที่ฉันเข้าใจฟิลด์ OneToOne เป็นแบบนั้น: แบบหนึ่งต่อหนึ่ง มันไม่จำเป็นต้องเป็น ดูตัวอย่างนี้ : สถานที่ไม่จำเป็นต้องเป็นร้านอาหาร
osa

21
คำตอบนี้บอกว่า "มีความแตกต่าง" แล้วตั้งชื่อความแตกต่างหนึ่ง มีคนอื่นไหม?
Chris Martin

6
ฉันสงสัยเหมือนกับคริส มันเป็นน้ำตาลเชิงซ้อนหรือไม่นั้นมีความแตกต่างบางประการในการเข้าถึงข้อมูลซึ่งนำไปสู่ความแตกต่างด้านประสิทธิภาพหรือไม่?
Carlos

4
มีเหตุผลพื้นฐานทำไม Django ไม่สามารถมีกฎดังกล่าวว่าถ้าต่างประเทศที่สำคัญเป็นเอกลักษณ์และไม่เป็นโมฆะแล้วe.carยังทำงาน?
Ciro Santilli 10 冠状病六四事件法轮功

4
ดังนั้น ... เมื่อจะหนึ่งได้ต้องการที่จะใช้ForeignKeyกับunique=Trueมากกว่าOneToOneField? ฉันเห็นในคำถามอื่น ๆ ที่ Django เตือนแม้กระทั่งว่าผลOneToOneFieldประโยชน์ของคนที่ดีที่สุดมักจะ การย้อนกลับQuerySetจะไม่มีองค์ประกอบมากกว่าหนึ่งองค์ประกอบใช่ไหม
แอนดี้

121

ForeignKey มีไว้สำหรับหนึ่งต่อหลายคนดังนั้นวัตถุรถยนต์อาจมีล้อหลายล้อแต่ละล้อที่มีกุญแจต่างประเทศเป็นของรถ OneToOneField จะเป็นเหมือนเครื่องมือที่วัตถุรถยนต์สามารถมีได้เพียงอันเดียว


4
ขอบคุณ Dose OneToOneField (someModel) หมายถึง ForeignKey (SomeModel, unique = True) หรือไม่
redice

9
ใช่: 'OneToOneField เป็นหลักเหมือนกับ ForeignKey โดยมีข้อยกเว้นที่มักจะมีข้อ จำกัด "ที่ไม่เหมือนใคร" และความสัมพันธ์แบบย้อนกลับจะส่งคืนวัตถุที่ชี้ไปเสมอ (เนื่องจากจะมีเพียงหนึ่ง) แทนที่จะส่งกลับ รายการ.'
ด่านบรีน

1
มีรถยนต์หลายคันที่มีเครื่องยนต์เหมือนกันหรือไม่
Oleg Belousov

3
@OlegTikhonov พวกเขาอาจมีสำเนาของการออกแบบเครื่องยนต์เหมือนกัน แต่ฉันต้องการดูตัวอย่างที่มีรถยนต์หลายคันที่ใช้เครื่องยนต์ร่วมกันแบบเดียวกัน
Dan Breen

3
มีความสับสนเล็กน้อยเกี่ยวกับคำศัพท์ในคำตอบนี้ ForeignKey ไม่ได้เป็นอย่างใดอย่างหนึ่งต่อหลายคน แต่มันก็เป็นความสัมพันธ์แบบหลายตามเอกสาร Django อย่างเป็นทางการ: docs.djangoproject.com/en/2.0/ref/models/fields/...
Kutay Demireren

45

วิธีที่ดีที่สุดและมีประสิทธิภาพมากที่สุดในการเรียนรู้สิ่งใหม่คือการดูและศึกษาตัวอย่างการใช้งานจริง สมมติว่าคุณต้องการสร้างบล็อกใน django สักครู่หนึ่งเพื่อให้นักข่าวสามารถเขียนและเผยแพร่บทความข่าวได้ เจ้าของหนังสือพิมพ์ออนไลน์ต้องการให้นักข่าวแต่ละคนของเขาเผยแพร่บทความได้มากเท่าที่ต้องการ แต่ไม่ต้องการให้นักข่าวต่างกันทำงานบทความเดียวกัน ซึ่งหมายความว่าเมื่อผู้อ่านไปและอ่านบทความพวกเขาจะเห็นผู้เขียนคนเดียวในบทความ

ตัวอย่างเช่น: บทความโดย John, บทความโดย Harry, บทความโดย Rick คุณไม่สามารถมีบทความโดย Harry & Rick เพราะหัวหน้าไม่ต้องการให้ผู้เขียนสองคนขึ้นไปทำงานในบทความเดียวกัน

เราจะแก้ปัญหานี้ด้วยความช่วยเหลือของ django ได้อย่างไร? กุญแจสำคัญในการแก้ปัญหาของปัญหานี้คือ ForeignKeyDjango

ต่อไปนี้เป็นรหัสเต็มรูปแบบซึ่งสามารถใช้ในการใช้ความคิดของเจ้านายของเรา

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

รันpython manage.py syncdbเพื่อรันโค้ด sql และสร้างตารางสำหรับแอปของคุณในฐานข้อมูลของคุณ จากนั้นใช้python manage.py shellเพื่อเปิดเปลือกงูหลาม

สร้างวัตถุ Reporter R1

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

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

In [51]: R1.save()

สร้างวัตถุบทความ A1

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

In [6]: A1.save()

จากนั้นใช้โค้ดต่อไปนี้เพื่อรับชื่อนักข่าว

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

ตอนนี้สร้าง Reporter object R2 โดยรันรหัสไพ ธ อนต่อไปนี้

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

In [10]: R2.save()

ทีนี้ลองเพิ่ม R2 ไปที่ Article Article A1

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

มันไม่ทำงานและคุณจะได้รับ AttributeError ว่าวัตถุ 'Reporter' ไม่มีแอตทริบิวต์ 'เพิ่ม'

ในขณะที่คุณสามารถเห็นวัตถุบทความไม่สามารถเกี่ยวข้องกับวัตถุผู้สื่อข่าวมากกว่าหนึ่ง

แล้ว R1 ล่ะ เราสามารถแนบวัตถุของบทความมากกว่าหนึ่งวัตถุได้หรือไม่

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

ตัวอย่างการปฏิบัตินี้แสดงให้เราเห็นว่า django ForeignKeyใช้เพื่อกำหนดความสัมพันธ์แบบหนึ่งต่อหนึ่ง

OneToOneField ใช้เพื่อสร้างความสัมพันธ์แบบหนึ่งต่อหนึ่ง

เราสามารถใช้reporter = models.OneToOneField(Reporter)ในไฟล์ models.py ข้างต้น แต่มันจะไม่เป็นประโยชน์ในตัวอย่างของเราเนื่องจากผู้เขียนจะไม่สามารถโพสต์บทความมากกว่าหนึ่งบทความ

ทุกครั้งที่คุณต้องการโพสต์บทความใหม่คุณจะต้องสร้างวัตถุ Reporter ใหม่ ใช้เวลานานใช่มั้ย

ฉันขอแนะนำให้ลองตัวอย่างด้วยOneToOneFieldและตระหนักถึงความแตกต่าง ผมค่อนข้างแน่ใจว่าหลังจากที่ตัวอย่างนี้คุณสมบูรณ์จะทราบความแตกต่างระหว่าง Django OneToOneFieldและ ForeignKeyDjango


ฉันชอบสิ่งนี้. ความแตกต่างพื้นฐานระหว่าง OneToOne และ ForeignKey คือความสัมพันธ์แบบหนึ่งต่อหนึ่งและแบบหนึ่งต่อความสัมพันธ์จำนวนมาก คุณสามารถใช้ ForeignKey และ unique = True เพื่อทำแบบตัวต่อตัวความแตกต่างที่ละเอียดได้ระบุไว้ในคำตอบของ Matthew
FrankZhu

13

OneToOneField (หนึ่งต่อหนึ่ง) ตระหนักถึงการวางแนวของวัตถุความคิดของการจัดองค์ประกอบในขณะที่ ForeignKey (หนึ่งต่อหลายคน) เกี่ยวข้องกับ agregation


3
การเปรียบเทียบที่ดี แต่ก็ไม่ได้เป็นเช่นนั้นเสมอไป มีบางกรณีที่ไม่เหมาะกับคำอธิบายนี้ พูด Let 's ตัวอย่างเช่นเรามีการเรียนและPatient สามารถมีหลายs แต่สามารถอยู่เพียงคนเดียว เมื่อถูกลบs ทั้งหมดจะถูกลบด้วย พวกเขาไม่สามารถอยู่ได้โดยไม่ต้อง OrganPatientOrganOrganPatientPatientOrganPatient
cezar

4

นอกจากนี้ยังOneToOneFieldมีประโยชน์ที่จะใช้เป็นคีย์หลักเพื่อหลีกเลี่ยงการทำสำเนาคีย์ อย่างใดอย่างหนึ่งอาจไม่มีการเปิดเผยอัตโนมัติโดยนัย / ชัดเจน

models.AutoField(primary_key=True)

แต่ใช้OneToOneFieldเป็นคีย์หลักแทน (ลองนึกภาพUserProfileรุ่นเป็นต้น):

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

3

เมื่อคุณเข้าถึง OneToOneField คุณจะได้รับค่าของฟิลด์ที่คุณสอบถาม ในตัวอย่างนี้ฟิลด์ 'ชื่อเรื่อง' ของรูปแบบหนังสือคือ OneToOneField:

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

เมื่อคุณเข้าถึง ForeignKey คุณจะได้รับวัตถุโมเดลที่เกี่ยวข้องซึ่งคุณสามารถ preform แบบสอบถามเพิ่มเติมได้ ในตัวอย่างนี้ฟิลด์ 'ผู้จัดพิมพ์' ของรูปแบบหนังสือเล่มเดียวกันคือ ForeignKey (สัมพันธ์กับคำจำกัดความของรูปแบบคลาสสำนักพิมพ์)

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

ด้วยการค้นหาเขตข้อมูล ForeignKey ก็ใช้วิธีอื่นเช่นกัน แต่ก็แตกต่างกันเล็กน้อยเนื่องจากลักษณะความสัมพันธ์ที่ไม่สมมาตร

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

book_set เป็นฉากหลังของฉากและสามารถกรองและแบ่งย่อยเป็นชุดอื่น ๆ ได้ ชื่อแอ็ตทริบิวต์ book_set ถูกสร้างขึ้นโดยต่อท้ายชื่อโมเดลตัวพิมพ์เล็กเป็น _set


1

OneToOneField: หากตารางที่สองเกี่ยวข้องกับ

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

table2 จะมีเพียงหนึ่งระเบียนที่สอดคล้องกับค่า pk ของ table1 เช่น table2_col1 จะมีค่าที่ไม่ซ้ำกันเท่ากับ pk ของตาราง

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

table2 อาจมีมากกว่าหนึ่งบันทึกที่สอดคล้องกับค่า pk ของ table1


1

ForeignKey อนุญาตให้คุณรับคลาสย่อยคือนิยามของคลาสอื่น แต่ OneToOneFields ไม่สามารถทำได้และไม่สามารถแนบกับตัวแปรหลายตัวได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.