Яка різниця між Джанго 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може мати багато Organs, але a Organможе належати лише одному Patient. Коли Patientвидалено, всі Organs також видаляються. Вони не можуть існувати без 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.