В чем разница между django OneToOneField и ForeignKey?

Ответы:

507

Будьте осторожны, чтобы понять, что между OneToOneField(SomeModel)и есть некоторые различия ForeignKey(SomeModel, unique=True). Как указано в Полном руководстве по Django :

OneToOneField

Отношения один-к-одному. Концептуально это похоже на ForeignKeywith unique=True, но «обратная» сторона отношения будет напрямую возвращать один объект.

В отличие от OneToOneField«обратного» отношения, ForeignKey«обратное» отношение возвращает a 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
Этот ответ говорит: «Есть некоторые различия», а затем называет одно отличие. Есть ли другие?
Крис Мартин
6
Мне интересно так же, как Крис. Является ли это просто синтаксическим сахаром, есть ли какое-то основное различие в доступе к данным, приводящее к различиям в производительности?
Карлос
4
Есть ли фундаментальная причина, по которой у Django не может быть правила, которое гласит, что если внешний ключ уникален и не равен нулю, то e.carтакже работает?
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
4
Итак ... когда бы вы захотели использовать скорее ForeignKeyс unique=True, чем с OneToOneField? В других вопросах я вижу, что Джанго даже предупреждает, что OneToOneFieldобычно лучше всего служат интересам. На реверсе QuerySetникогда не будет более одного элемента, верно?
Энди
121

ForeignKey предназначен для одного ко многим, поэтому у объекта Car может быть много Колес, каждое из которых имеет ForeignKey для Автомобиля, которому он принадлежит. OneToOneField был бы похож на Engine, где объект Car может иметь один и только один объект.

Дэн Брин
источник
4
спасибо, Dose OneToOneField (someModel) означает ForeignKey (SomeModel, unique = True)?
Ředice
9
Да: «OneToOneField по сути такой же, как ForeignKey, за исключением того, что оно всегда содержит« уникальное »ограничение, а обратное отношение всегда возвращает объект, на который указывает (поскольку он будет только один), а не возвращает список.'
Дэн Брин
1
Как насчет нескольких автомобилей, имеющих один и тот же двигатель?
Олег Белоусов
3
@OlegTikhonov У них может быть копия одного и того же дизайна двигателя, но я хотел бы увидеть случай, когда несколько автомобилей используют один и тот же физический двигатель.
Дэн Брин
3
В этом ответе есть небольшая путаница с терминами. ForeignKey - это не один-ко-многим, но это отношение один-ко-многим, согласно официальной документации django: docs.djangoproject.com/en/2.0/ref/models/fields/…
Kutay Demireren
45

Лучший и самый эффективный способ узнать что-то новое - это увидеть и изучить примеры из реальной жизни. Предположим на мгновение, что вы хотите создать блог в 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.

jetbird13
источник
Мне это нравится. Принципиальное различие между OneToOne и ForeignKey - это отношения один к одному и один ко многим. Вы можете использовать ForeignKey и unique = True, чтобы сделать один-к-одному, тонкое различие указано в ответе Мэтью.
ФранкЖу
13

OneToOneField (один-к-одному) реализует в объектной ориентации понятие композиции, в то время как ForeignKey (один-ко-многим) относится к агрегации.

andrers52
источник
3
Хорошая аналогия, но это не всегда так. Есть некоторые крайние случаи, которые не вписываются в это объяснение. Скажем, например, у нас есть классы Patientи Organ. Patientможет иметь много Organs, но Organможет принадлежать только одному Patient. Когда Patientудаляется, все Organs тоже удаляются. Они не могут существовать без Patient.
Цезарь
4

Также OneToOneFieldполезно использовать в качестве первичного ключа, чтобы избежать дублирования ключа. Можно не иметь неявного / явного автополя

models.AutoField(primary_key=True)

но OneToOneFieldвместо этого используйте в качестве первичного ключа (представьте UserProfileмодель):

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')
Дмитрий Синцов
источник
3

Когда вы получаете доступ к 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.

Ага.
источник
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 не может этого сделать, и он не может быть присоединен к нескольким переменным

Bitdom8
источник