Яка різниця між Джанго OneToOneField
і ForeignKey
?
Яка різниця між Джанго OneToOneField
і ForeignKey
?
Відповіді:
Будьте уважні, щоб усвідомити, що є і деякі відмінності між OneToOneField(SomeModel)
та ForeignKey(SomeModel, unique=True)
. Як зазначено в Посібнику з Джанго :
OneToOneField
Стосунки один на один. Концептуально це схоже на
ForeignKey
сunique=True
, але "зворотна" сторона відношення безпосередньо поверне один об'єкт.
На відміну від OneToOneField
"зворотного" відношення, ForeignKey
"зворотне" відношення повертає a QuerySet
.
Наприклад, якщо у нас є дві наступні моделі (повний код моделі нижче):
Car
використовує модель OneToOneField(Engine)
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
e.car
також працює?
ForeignKey
з , unique=True
а не OneToOneField
? Я бачу в інших питаннях, що Джанго навіть попереджає, що OneToOneField
зазвичай найкраще служить інтересам. Реверс QuerySet
ніколи не матиме більше одного елемента, правда?
ForeignKey - це один для багатьох, тому об'єкт "Автомобіль" може мати багато коліс, кожне колесо має іноземний ключ до автомобіля, якому воно належить. OneToOneField буде подібний до двигуна, де об'єкт "Автомобіль" може мати один і єдиний.
Найкращий і найефективніший спосіб дізнатися нове - це бачити та вивчати практичні приклади реального світу. Припустимо на мить, що ви хочете створити блог у джанго, де журналісти можуть писати та публікувати статті новин. Власник інтернет-газети хоче дозволити кожному своєму репортеру опублікувати стільки статей, скільки хоче, але не хоче, щоб різні репортери працювали над однією статтею. Це означає, що коли читачі йдуть і прочитають статтю, у статті з’явиться лише один автор.
Наприклад: Стаття Джона, Стаття Гаррі, Стаття Ріка. Ви не можете мати Статтю від Гаррі та Ріка, оскільки начальник не хоче, щоб над однією статтею працювали два чи більше авторів.
Як ми можемо вирішити цю «проблему» за допомогою джанго? Запорукою вирішення цієї проблеми є джанго 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
.
OneToOneField (один на один) реалізує в об'єктній орієнтації поняття складу, тоді як ForeignKey (один на багато) стосується узгодження.
Patient
та Organ
. Patient
може мати багато Organ
s, але a Organ
може належати лише одному Patient
. Коли Patient
видалено, всі Organ
s також видаляються. Вони не можуть існувати без Patient
.
Також OneToOneField
корисно використовуватись як основний ключ, щоб уникнути дублювання ключів. Можливо, у них немає неявного / явного автополя
models.AutoField(primary_key=True)
але використовуйте OneToOneField
замість цього основний ключ (уявіть, наприклад, UserProfile
модель):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
Коли ви отримуєте доступ до 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.
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.