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

300

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

Теги:
django-models

7 ответов

396
Лучший ответ

Будьте осторожны, чтобы понять, что существуют некоторые различия между 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
  • 5
    @MarkPNeyer: насколько я понимаю, поле OneToOne - это всего лишь одно к одному. Это не должно быть на. Посмотрите этот пример : место не обязательно должно быть рестораном.
  • 8
    Этот ответ говорит: «Есть некоторые различия», а затем называет одно отличие. Есть ли другие?
Показать ещё 4 комментария
92

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

  • 2
    спасибо, Dose OneToOneField (someModel) означает ForeignKey (SomeModel, unique = True)?
  • 5
    Да: «OneToOneField по сути такой же, как ForeignKey, за исключением того, что оно всегда содержит« уникальное »ограничение, а обратное отношение всегда возвращает объект, на который указывает (поскольку он будет только один), а не возвращает список.'
Показать ещё 4 комментария
26

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

11

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

  • 0
    Хорошая аналогия, но это не всегда так. Есть некоторые крайние случаи, которые не вписываются в это объяснение. Скажем, например, у нас есть классы Patient и Organ . Patient может иметь много Organ , но Organ может принадлежать только одному Patient . При удалении Patient удаляются также все Organ . Они не могут существовать без Patient .
2

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

models.AutoField(primary_key=True)

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

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

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

0

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.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню