Câu trả lời:
Hãy cẩn thận để nhận ra rằng có một số khác biệt giữa OneToOneField(SomeModel)
và 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
ForeignKey
vớiunique=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 OneToOneField
mối quan hệ "đảo ngược", mối quan hệ ForeignKey
"đảo ngược" trả về a QuerySet
.
Ví dụ: nếu chúng ta có hai mô hình sau (mã mô hình đầy đủ bên dưới):
Car
sử dụng mô hình OneToOneField(Engine)
Car2
sử dụng mô hình ForeignKey(Engine2, unique=True)
Từ bên trong python manage.py shell
thự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>
ForeignKey
với unique=True
ví 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>]
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
cũng hoạt động?
ForeignKey
với unique=True
hơ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 OneToOneField
lợi ích của một người thường phục vụ tốt nhất. Ngược lại QuerySet
sẽ không bao giờ có nhiều hơn một yếu tố, phải không?
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.
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 OneToOneField
và 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 OneToOneField
và django ForeignKey
.
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.
Patient
và Organ
. Patient
có thể có nhiều Organ
s, nhưng một chỉ Organ
có thể thuộc về một Patient
. Khi Patient
bị xóa, tất cả Organ
s cũng bị xóa. Chúng không thể tồn tại mà không có a Patient
.
Cũng OneToOneField
rấ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 OneToOneField
làm khóa chính thay thế ( UserProfile
ví dụ mô hình tưởng tượng ):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
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.
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.