Какая разница между django OneToOneField и ForeignKey?

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

Ответ 1

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

OneToOneField

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

В отличие от OneToOneField "обратной" связи, A ForeignKey "обратный" отношение возвращает 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

Ответ 2

A ForeignKey для одного-ко-многим, поэтому объект Car может иметь много колес, каждое колесо имеет ForeignKey для Car, к которому он принадлежит. OneToOneField будет похож на движок, в котором объект Car может иметь один и только один.

Ответ 3

Лучший и самый эффективный способ узнать новое - увидеть и изучить реальные практические примеры. Предположим на мгновение, что вы хотите создать блог в django, где журналисты могут писать и публиковать новостные статьи. Владелец интернет-газеты хочет разрешить каждому из своих репортеров публиковать как можно больше статей, но не хочет, чтобы разные журналисты работали над одной и той же статьей. Это означает, что, когда читатели идут и читают статью, в статье будет только один автор.

Например: статья Джона, статья Гарри, статья Рика. Вы не можете иметь статью Гарри и Рика, потому что босс не хочет, чтобы два или более автора работали над одной и той же статьей.

Как мы можем решить эту "проблему" с помощью 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()

Создайте объект статьи 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 в объект статьи A1.

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

Это не работает, и вы получите AttributeError, говорящий, что объект 'Reporter' не имеет атрибута 'add'.

Как вы можете видеть, объект Article не может быть связан с более чем одним объектом Reporter.

Как насчет R1? Можем ли мы присоединить к нему более одного объекта статьи?

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.

Ответ 4

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

Ответ 5

Когда вы получаете доступ к OneToOneField, вы получаете значение поля, которое вы запрашивали. В этом примере поле "title" книги модели - OneToOneField:

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

Когда вы обращаетесь к ForeignKey, вы получаете связанный объект модели, из которого вы можете запросить дополнительные запросы. В этом примере одно и то же поле книги "издатель" - это ForeignKey (соотносится с определением модели класса издателя):

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

Ответ 6

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

models.AutoField(primary_key=True)

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

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

Ответ 7

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.

Ответ 8

ForeignKey позволяет получать подклассы, если это определение другого класса, но OneToOneFields не может этого сделать, и он не может быть присоединен к нескольким переменным