Будьте осторожны, чтобы понять, что между OneToOneField(SomeModel)
и есть некоторые различия ForeignKey(SomeModel, unique=True)
. Как указано в Полном руководстве по Django :
OneToOneField
Отношения один-к-одному. Концептуально это похоже на ForeignKey
with unique=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
.Ниже приведен полный код, который можно использовать для реализации идеи нашего босса.
Запустите
python manage.py syncdb
для выполнения кода sql и построения таблиц для вашего приложения в вашей базе данных. Затем используйте,python manage.py shell
чтобы открыть оболочку Python.Создайте объект Reporter R1.
Создайте объект Article A1.
Затем используйте следующий фрагмент кода, чтобы получить имя репортера.
Теперь создайте объект Reporter R2, выполнив следующий код Python.
Теперь попробуйте добавить R2 к объекту Article A1.
Это не работает, и вы получите AttributeError о том, что объект 'Reporter' не имеет атрибута 'add'.
Как видите, объект Article не может быть связан с несколькими объектами Reporter.
Что насчет R1? Можем ли мы прикрепить к нему более одного объекта Article?
Этот практический пример показывает нам, что django
ForeignKey
используется для определения отношений многие-к-одному.OneToOneField
используется для создания отношений один-к-одному.Мы можем использовать
reporter = models.OneToOneField(Reporter)
в приведенном выше файле models.py, но он не будет полезен в нашем примере, так как автор не сможет опубликовать более одной статьи.Каждый раз, когда вы хотите опубликовать новую статью, вам нужно будет создать новый объект Reporter. Это отнимает много времени, не так ли?
Я настоятельно рекомендую попробовать пример с
OneToOneField
и понять разницу. Я вполне уверен, что после этого примера вы полностью узнаете разницу между djangoOneToOneField
и djangoForeignKey
.источник
OneToOneField (один-к-одному) реализует в объектной ориентации понятие композиции, в то время как ForeignKey (один-ко-многим) относится к агрегации.
источник
Patient
иOrgan
.Patient
может иметь многоOrgan
s, ноOrgan
может принадлежать только одномуPatient
. КогдаPatient
удаляется, всеOrgan
s тоже удаляются. Они не могут существовать безPatient
.Также
OneToOneField
полезно использовать в качестве первичного ключа, чтобы избежать дублирования ключа. Можно не иметь неявного / явного автополяно
OneToOneField
вместо этого используйте в качестве первичного ключа (представьтеUserProfile
модель):источник
Когда вы получаете доступ к OneToOneField, вы получаете значение поля, которое вы запросили. В этом примере поле 'title' модели книги - это OneToOneField:
Когда вы получаете доступ к ForeignKey, вы получаете связанный объект модели, к которому вы можете затем выполнить дальнейшие запросы. В этом примере поле 'publisher' той же модели книги - это ForeignKey (соответствует определению модели класса Publisher):
С полями ForeignKey запросы работают и в другом направлении, но они немного отличаются из-за несимметричного характера отношений.
За кулисами book_set является просто QuerySet и может быть отфильтрован и разрезан, как любой другой QuerySet. Имя атрибута book_set генерируется путем добавления названия модели в нижнем регистре к _set.
источник
OneToOneField: если вторая таблица связана с
table2 будет содержать только одну запись, соответствующую pk-значению table1, т.е. table2_col1 будет иметь уникальное значение, равное pk таблицы
table2 может содержать более одной записи, соответствующей значению pk для table1.
источник
ForeignKey позволяет получать подклассы, если это определение другого класса, но OneToOneFields не может этого сделать, и он не может быть присоединен к нескольким переменным
источник