В чем разница между django OneToOneField и ForeignKey? - PullRequest
337 голосов
/ 03 мая 2011

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

Ответы [ 7 ]

437 голосов
/ 05 мая 2011

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

OneToOneField

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

В отличие от OneToOneField «обратный»отношение, 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
98 голосов
/ 03 мая 2011

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

31 голосов
/ 14 ноября 2014

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

Создание объекта 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.

11 голосов
/ 28 апреля 2012

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

2 голосов
/ 17 ноября 2015

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

models.AutoField(primary_key=True)

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

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')
2 голосов
/ 12 февраля 2014

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

0 голосов
/ 28 марта 2019

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')

таблица2 может содержать более одной записи, соответствующей значению pk таблицы 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...