Ответы:
Будьте осторожны, чтобы понять, что между OneToOneField(SomeModel)
и есть некоторые различия ForeignKey(SomeModel, unique=True)
. Как указано в Полном руководстве по Django :
OneToOneField
Отношения один-к-одному. Концептуально это похоже на
ForeignKey
withunique=True
, но «обратная» сторона отношения будет напрямую возвращать один объект.
В отличие от OneToOneField
«обратного» отношения, ForeignKey
«обратное» отношение возвращает a QuerySet
.
Например, если у нас есть следующие две модели (полный код модели ниже):
Car
модель использует OneToOneField(Engine)
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
e.car
также работает?
ForeignKey
с unique=True
, чем с OneToOneField
? В других вопросах я вижу, что Джанго даже предупреждает, что OneToOneField
обычно лучше всего служат интересам. На реверсе QuerySet
никогда не будет более одного элемента, верно?
ForeignKey предназначен для одного ко многим, поэтому у объекта Car может быть много Колес, каждое из которых имеет ForeignKey для Автомобиля, которому он принадлежит. OneToOneField был бы похож на Engine, где объект Car может иметь один и только один объект.
Лучший и самый эффективный способ узнать что-то новое - это увидеть и изучить примеры из реальной жизни. Предположим на мгновение, что вы хотите создать блог в Django, где журналисты могут писать и публиковать новостные статьи. Владелец интернет-газеты хочет, чтобы каждый из его репортеров опубликовал столько статей, сколько они хотят, но не хочет, чтобы разные репортеры работали над одной и той же статьей. Это означает, что когда читатели пойдут и прочитают статью, они увидят в ней только одного автора.
Например: статья Джона, статья Гарри, статья Рика. Вы не можете иметь статью Гарри и Рика, потому что босс не хочет, чтобы два или более авторов работали над одной и той же статьей.
Как мы можем решить эту «проблему» с помощью Django? Ключом к решению этой проблемы является Джанго ForeignKey
.
Ниже приведен полный код, который можно использовать для реализации идеи нашего босса.
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
чтобы открыть оболочку Python.
Создайте объект Reporter R1.
In [49]: from thepub.models import Reporter, Article
In [50]: R1 = Reporter(first_name='Rick')
In [51]: R1.save()
Создайте объект Article 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 R2, выполнив следующий код Python.
In [9]: R2 = Reporter.objects.create(first_name='Harry')
In [10]: R2.save()
Теперь попробуйте добавить R2 к объекту Article A1.
In [13]: A1.reporter.add(R2)
Это не работает, и вы получите AttributeError о том, что объект 'Reporter' не имеет атрибута 'add'.
Как видите, объект Article не может быть связан с несколькими объектами Reporter.
Что насчет R1? Можем ли мы прикрепить к нему более одного объекта Article?
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
и django ForeignKey
.
OneToOneField (один-к-одному) реализует в объектной ориентации понятие композиции, в то время как ForeignKey (один-ко-многим) относится к агрегации.
Patient
и Organ
. Patient
может иметь много Organ
s, но Organ
может принадлежать только одному Patient
. Когда Patient
удаляется, все Organ
s тоже удаляются. Они не могут существовать без Patient
.
Также OneToOneField
полезно использовать в качестве первичного ключа, чтобы избежать дублирования ключа. Можно не иметь неявного / явного автополя
models.AutoField(primary_key=True)
но OneToOneField
вместо этого используйте в качестве первичного ключа (представьте UserProfile
модель):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
Когда вы получаете доступ к OneToOneField, вы получаете значение поля, которое вы запросили. В этом примере поле 'title' модели книги - это OneToOneField:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
Когда вы получаете доступ к ForeignKey, вы получаете связанный объект модели, к которому вы можете затем выполнить дальнейшие запросы. В этом примере поле 'publisher' той же модели книги - это ForeignKey (соответствует определению модели класса Publisher):
>>> 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 является просто QuerySet и может быть отфильтрован и разрезан, как любой другой QuerySet. Имя атрибута book_set генерируется путем добавления названия модели в нижнем регистре к _set.
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.