Яка різниця між django OneToOneField та ForeignKey?


Відповіді:


507

Будьте уважні, щоб усвідомити, що є і деякі відмінності між OneToOneField(SomeModel)та ForeignKey(SomeModel, unique=True). Як зазначено в Посібнику з Джанго :

OneToOneField

Стосунки один на один. Концептуально це схоже на ForeignKeyс 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
Чи є принципова причина, чому Джанго не міг би мати таке правило, що якщо зовнішній ключ унікальний, а не нульовий, то він e.carтакож працює?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4
Так що ... якщо б один навіть хочете використовувати ForeignKeyз , unique=Trueа не OneToOneField? Я бачу в інших питаннях, що Джанго навіть попереджає, що OneToOneFieldзазвичай найкраще служить інтересам. Реверс QuerySetніколи не матиме більше одного елемента, правда?
Енді,

121

ForeignKey - це один для багатьох, тому об'єкт "Автомобіль" може мати багато коліс, кожне колесо має іноземний ключ до автомобіля, якому воно належить. OneToOneField буде подібний до двигуна, де об'єкт "Автомобіль" може мати один і єдиний.


4
дякую, Dose OneToOneField (someModel) означає ForeignKey (SomeModel, унікальний = True)?
redice

9
Так: 'OneToOneField по суті є тим же, що і ForeignKey, за винятком того, що завжди несе з собою "унікальне" обмеження, і зворотне відношення завжди повертає об'єкт, на який вказував (оскільки буде лише один), а не повертає список. '
Ден Брін

1
А як щодо кількох автомобілів, що мають однаковий двигун?
Олег Білоусов

3
@OlegTikhonov У них може бути копія тієї ж конструкції двигуна, але я хотів би побачити приклад, коли декілька автомобілів мають спільний фізичний двигун.
Ден Брін

3
Трохи плутанини щодо термінів у цій відповіді. ForeignKey - це не один-багато-багато, але це відносини "багато в одному" згідно з офіційною документацією про джанго
Кутай Деміререн

45

Найкращий і найефективніший спосіб дізнатися нове - це бачити та вивчати практичні приклади реального світу. Припустимо на мить, що ви хочете створити блог у джанго, де журналісти можуть писати та публікувати статті новин. Власник інтернет-газети хоче дозволити кожному своєму репортеру опублікувати стільки статей, скільки хоче, але не хоче, щоб різні репортери працювали над однією статтею. Це означає, що коли читачі йдуть і прочитають статтю, у статті з’явиться лише один автор.

Наприклад: Стаття Джона, Стаття Гаррі, Стаття Ріка. Ви не можете мати Статтю від Гаррі та Ріка, оскільки начальник не хоче, щоб над однією статтею працювали два чи більше авторів.

Як ми можемо вирішити цю «проблему» за допомогою джанго? Запорукою вирішення цієї проблеми є джанго 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для відкриття оболонки пітона.

Створіть об’єкт Reporter R1.

In [49]: from thepub.models import Reporter, Article

In [50]: R1 = Reporter(first_name='Rick')

In [51]: R1.save()

Створіть об’єкт А1.

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, що говорить: "Репортер" об'єкта не має атрибута "додати".

Як ви бачите, об'єкт 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>]

Цей практичний приклад показує нам, що джанго ForeignKeyвикористовується для визначення відносин між собою.

OneToOneField використовується для створення взаємовідносин один на один.

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

Щоразу, коли ви хочете опублікувати нову статтю, вам доведеться створювати новий об’єкт "Репортер". Це забирає багато часу, чи не так?

Я настійно рекомендую спробувати приклад з OneToOneFieldі зрозуміти різницю. Я майже впевнений, що після цього прикладу ви повністю пізнаєте різницю між джанго OneToOneFieldта джанго ForeignKey.


Мені це подобається. Принципова відмінність OneToOne від ForeignKey - це відносини один до одного та один до багатьох. Ви можете використовувати ForeignKey та унікальний = True, щоб робити один на один, тонка різниця вказана у відповіді Метью.
FrankZhu

13

OneToOneField (один на один) реалізує в об'єктній орієнтації поняття складу, тоді як ForeignKey (один на багато) стосується узгодження.


3
Приємна аналогія, але це не завжди так. Є деякі крайові випадки, які не вписуються в це пояснення. Скажімо, наприклад, у нас є класи Patientта Organ. Patientможе мати багато Organs, але a Organможе належати лише одному Patient. Коли Patientвидалено, всі Organs також видаляються. Вони не можуть існувати без Patient.
cezar

4

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

models.AutoField(primary_key=True)

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

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

3

Коли ви отримуєте доступ до OneToOneField, ви отримуєте значення поля, яке ви запитували. У цьому прикладі поле 'назва' книжкової моделі - це OneToOneField:

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

Коли ви отримуєте доступ до ForeignKey, ви отримуєте відповідний об'єкт моделі, до якого ви зможете підготувати подальші запити. У цьому прикладі поле "видавець" тієї самої моделі книги - це 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 не може цього зробити, і це не можна приєднати до кількох змінних

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.