Sự khác biệt giữa django OneToOneField và ForeignKey là gì?


Câu trả lời:


507

Hãy cẩn thận để nhận ra rằng có một số khác biệt giữa OneToOneField(SomeModel)ForeignKey(SomeModel, unique=True). Như đã nêu trong Hướng dẫn dứt khoát cho Django :

OneToOneField

Một mối quan hệ một-một. Về mặt lý thuyết, điều này cũng tương tự như một ForeignKeyvới unique=True, nhưng "ngược" bên của mối quan hệ sẽ trực tiếp trả về một đối tượng duy nhất.

Ngược lại với OneToOneFieldmối quan hệ "đảo ngược", mối quan hệ ForeignKey"đảo ngược" trả về a QuerySet.

Thí dụ

Ví dụ: nếu chúng ta có hai mô hình sau (mã mô hình đầy đủ bên dưới):

  1. Car sử dụng mô hình OneToOneField(Engine)
  2. Car2 sử dụng mô hình ForeignKey(Engine2, unique=True)

Từ bên trong python manage.py shellthực hiện như sau:

OneToOneField Thí dụ

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

ForeignKeyvới unique=Trueví dụ

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

Mã mẫu

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: theo như tôi hiểu, trường OneToOne chỉ là: một đối một. Nó không phải là lên. Xem ví dụ này : một nơi không phải là một nhà hàng.
osa

21
Câu trả lời này cho biết "có một số khác biệt", và sau đó đặt tên cho một sự khác biệt. Có những người khác?
Chris Martin

6
Tôi đang tự hỏi giống như Chris. Có phải nó chỉ đơn giản là cú pháp đường, có một số khác biệt cơ bản trong cách dữ liệu được truy cập, dẫn đến sự khác biệt hiệu suất?
Carlos

4
Có một lý do cơ bản tại sao Django không thể có một quy tắc như vậy nếu khóa ngoại là duy nhất và không null, thì nó e.carcũng hoạt động?
Ciro Santilli 郝海东 冠状 病 事件

4
Vì vậy, ... khi người ta sẽ còn muốn sử dụng một ForeignKeyvới unique=Truehơn một OneToOneField? Tôi thấy trong các câu hỏi khác mà Django thậm chí còn cảnh báo rằng OneToOneFieldlợi ích của một người thường phục vụ tốt nhất. Ngược lại QuerySetsẽ không bao giờ có nhiều hơn một yếu tố, phải không?
Andy

121

ForeignKey dành cho một người, vì vậy một đối tượng Xe có thể có nhiều Bánh xe, mỗi Bánh xe có Ngoại khóa cho Xe mà nó thuộc về. OneToOneField sẽ giống như một Động cơ, trong đó một đối tượng Xe có thể có một và chỉ một.


4
cảm ơn bạn, Dose OneToOneField (someModel) có nghĩa là ForeignKey (someModel, unique = True)?
lại

9
Có: 'OneToOneField về cơ bản giống như ForeignKey, ngoại trừ luôn mang một ràng buộc "duy nhất" với nó và quan hệ ngược lại luôn trả về đối tượng được chỉ (vì sẽ chỉ có một), thay vì trả về một danh sách.'
Dan Breen

1
Một vài chiếc xe có cùng động cơ thì sao?
Oleg Belousov

3
@OlegTikhonov Họ có thể có một bản sao của cùng một thiết kế động cơ, nhưng tôi muốn thấy một trường hợp trong đó một vài chiếc xe đang chia sẻ cùng một động cơ vật lý.
Dan Breen

3
Có một chút nhầm lẫn về các điều khoản trong câu trả lời này. ForeignKey không phải là một đối một mà là mối quan hệ nhiều-một theo tài liệu django chính thức: docs.djangoproject.com/en/2.0/ref/models/fields/iêu
Kutay Demireren

45

Cách tốt nhất và hiệu quả nhất để học những điều mới là xem và nghiên cứu các ví dụ thực tế trong thế giới thực. Giả sử trong một khoảnh khắc mà bạn muốn xây dựng một blog trong django nơi các phóng viên có thể viết và xuất bản các bài báo. Chủ sở hữu của tờ báo trực tuyến muốn cho phép mỗi phóng viên của mình xuất bản nhiều bài viết như họ muốn, nhưng không muốn các phóng viên khác nhau làm việc trên cùng một bài viết. Điều này có nghĩa là khi độc giả đi và đọc một bài báo họ sẽ chỉ có một tác giả trong bài viết.

Ví dụ: Bài viết của John, Bài viết của Harry, Bài viết của Rick. Bạn không thể có Bài viết của Harry & Rick vì ông chủ không muốn hai hoặc nhiều tác giả làm việc trên cùng một bài viết.

Làm thế nào chúng ta có thể giải quyết 'vấn đề' này với sự trợ giúp của django? Chìa khóa cho giải pháp của vấn đề này là django ForeignKey.

Sau đây là mã đầy đủ có thể được sử dụng để thực hiện ý tưởng của ông chủ của chúng tôi.

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

Chạy python manage.py syncdbđể thực thi mã sql và xây dựng các bảng cho ứng dụng của bạn trong cơ sở dữ liệu của bạn. Sau đó sử dụng python manage.py shellđể mở một vỏ trăn.

Tạo đối tượng Phóng viên R1.

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

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

In [51]: R1.save()

Tạo đối tượng Điều A1.

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

In [6]: A1.save()

Sau đó sử dụng đoạn mã sau để lấy tên của phóng viên.

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

Bây giờ tạo đối tượng Phóng viên R2 bằng cách chạy mã python sau.

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

In [10]: R2.save()

Bây giờ hãy thử thêm R2 vào đối tượng Article A1.

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

Nó không hoạt động và bạn sẽ nhận được một AttributionError nói rằng đối tượng 'Trình báo cáo' không có thuộc tính 'add'.

Như bạn có thể thấy một đối tượng Article không thể liên quan đến nhiều hơn một đối tượng Phóng viên.

Còn R1 thì sao? Chúng ta có thể đính kèm nhiều hơn một đối tượng Điều với nó không?

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

Ví dụ thực tế này cho chúng ta thấy rằng django ForeignKeyđược sử dụng để xác định các mối quan hệ nhiều-một.

OneToOneField được sử dụng để tạo mối quan hệ một-một.

Chúng tôi có thể sử dụng reporter = models.OneToOneField(Reporter)trong tệp mô hình ở trên nhưng nó sẽ không hữu ích trong ví dụ của chúng tôi vì tác giả sẽ không thể đăng nhiều hơn một bài viết.

Mỗi lần bạn muốn đăng một bài viết mới, bạn sẽ phải tạo một đối tượng Người báo cáo mới. Điều này là tốn thời gian, phải không?

Tôi rất khuyên bạn nên thử ví dụ với OneToOneFieldvà nhận ra sự khác biệt. Tôi khá chắc chắn rằng sau ví dụ này, bạn sẽ hoàn toàn biết sự khác biệt giữa django OneToOneFieldvà django ForeignKey.


Tôi thích điều này. Sự khác biệt cơ bản giữa OneToOne và ForeignKey là mối quan hệ một đối một và nhiều đối tượng. Bạn có thể sử dụng ForeignKey và unique = True để thực hiện từng bước một, sự khác biệt tinh tế được nêu trong câu trả lời của Matthew.
FrankZhu

13

OneToOneField (một đối một) nhận ra, trong định hướng đối tượng, khái niệm thành phần, trong khi ForeignKey (một-nhiều) liên quan đến sự kết hợp.


3
Tương tự tốt đẹp, nhưng không phải lúc nào cũng như vậy. Có một số trường hợp cạnh không phù hợp với giải thích này. Hãy nói ví dụ chúng ta có các lớp PatientOrgan. Patientcó thể có nhiều Organs, nhưng một chỉ Organcó thể thuộc về một Patient. Khi Patientbị xóa, tất cả Organs cũng bị xóa. Chúng không thể tồn tại mà không có a Patient.
cezar

4

Cũng OneToOneFieldrất hữu ích khi được sử dụng làm khóa chính để tránh trùng lặp khóa. Một người có thể không có tự động ẩn / rõ ràng

models.AutoField(primary_key=True)

nhưng sử dụng OneToOneFieldlàm khóa chính thay thế ( UserProfileví dụ mô hình tưởng tượng ):

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

3

Khi bạn truy cập OneToOneField, bạn sẽ nhận được giá trị của trường bạn đã truy vấn. Trong ví dụ này, trường 'tiêu đề' của mô hình sách là OneToOneField:

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

Khi bạn truy cập ForeignKey, bạn sẽ nhận được đối tượng mô hình có liên quan, sau đó bạn có thể tạo ra các truy vấn tiếp theo. Trong ví dụ này, trường 'nhà xuất bản' của cùng một mô hình sách là ForeignKey (tương quan với định nghĩa mô hình lớp Nhà xuất bản):

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

Với các truy vấn trường ForeignKey cũng hoạt động theo cách khác, nhưng chúng hơi khác nhau do tính chất không đối xứng của mối quan hệ.

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

Đằng sau hậu trường, book_set chỉ là một Truy vấn và có thể được lọc và cắt như bất kỳ Truy vấn nào khác. Tên thuộc tính book_set được tạo bằng cách nối thêm tên mô hình chữ thường vào _set.


1

OneToOneField: nếu bảng thứ hai có liên quan đến

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

bảng2 sẽ chỉ chứa một bản ghi tương ứng với giá trị pk của bảng1, tức là bảng2_col1 sẽ có giá trị duy nhất bằng pk của bảng

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

bảng2 có thể chứa nhiều hơn một bản ghi tương ứng với giá trị pk của bảng1.


1

ForeignKey cho phép bạn nhận các lớp con là định nghĩa của một lớp khác nhưng OneToOneFields không thể làm điều này và nó không thể gắn vào nhiều biến

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.