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

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

6 ответов


будьте осторожны, чтобы понять, что есть некоторые различия между 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

иностранный ключ для одного ко многим, поэтому объект автомобиля может иметь много колес, каждое колесо имеет иностранный ключ к автомобилю, к которому он принадлежит. OneToOneField будет похож на двигатель, где объект автомобиля может иметь один и только один.


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

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

как мы можем решить эту проблему с помощью Джанго? Ключом к решению этой проблемы является 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

Run 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 в статью объект А1.

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

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

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

насчет Р1? Можем ли мы прикрепить к нему несколько предметов?

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 используется для определения отношений "многие к одному".

это чтобы создать один-к-одному отношения.

можно использовать reporter = models.OneToOneField(Reporter) в вышеуказанном models.py файл, но он не будет полезен в нашем примере, так как автор не сможет опубликовать более одной статьи.

каждый раз, когда вы хотите опубликовать новую статью, вам придется создать новый объект Reporter. Это отнимает много времени, не так ли?

я настоятельно рекомендую попробовать пример с OneToOneField и поймете разницу. Я уверен, что после этого пример вы будете полностью знать разницу между django OneToOneField и Django ForeignKey.


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


при доступе к OneToOneField вы получаете значение поля, которое вы запросили. В этом примере поле "Название" книжной модели является полем 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.


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

models.AutoField(primary_key=True)

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

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