Зачем кому-то устанавливать primary_key=True для отношения "один к одному" (OneToOneField)?
Я смотрел видео по django-orm и инструктор заявил, что:
Мы должны установить
primary_key=True
для предотвращения дублирования строк в модели в отношениях "один к одному" (например: предотвращение наличия у пользователя нескольких профилей).
Я знаю, что это утверждение неверно! AFAIK, поле OneToOne
- это просто ForeignKey
с параметром unique
, установленным в True
. Но мне стало любопытно, и я заглянул в документацию Django. Уверен, что они используют primary=True
в своем примере.
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
Итак, зачем кому-то устанавливать primary_key=True
на отношение OneToOne
?
Я думаю, что это просто разумно иметь это поле в качестве первичного ключа, и за этим не стоит никакой технической подоплеки.
Это паттерн для реализации объектно-ориентированного наследования в реляционной базе данных, например, как это обсуждается в этой статье Oracle.
Действительно, это означает, что можно определить Place
, и для этой Place
создать модель Restaurant
. Она имеет OneToOneField(…)
к "родительской" модели. OneToOneField
предотвращает возможность определения двух (или более) Restaurant
для одной и той же Place
.
Обычно он определяется как первичный ключ, поскольку тогда он разделяет то же самое "пространство первичного кода", и он удаляет столбец, который иначе используется для выполнения сопоставления и таким образом сделает базу данных больше.
Django реализует это таким же образом. Если мы определим это как:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
pass
тогда он будет реализован как:
mysql> describe place;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(50) | NO | | NULL | |
| address | varchar(80) | NO | | NULL | |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> describe restaurant;
+--------------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------+------+-----+---------+-------+
| place_ptr_id | int | NO | PRI | NULL | |
+--------------+------+------+-----+---------+-------+
Таким образом, добавляется первичный ключ place_ptr_id
, который ссылается на таблицу place
. Это происходит от OneToOneField
, который Django добавляет к Restaurant
модели с именем place_ptr
.
Мы должны установить
.primary_key=True
для предотвращения дублирования строк в модели в отношениях "один к одному" (Например: предотвращение наличия у пользователя нескольких профилей)
Это не имеет смысла, поскольку OneToOneField
по сути является ForeignKey
с unique=True
[Django-doc]. Таким образом, это уже обеспечивается OneToOneField
, нет необходимости делать его первичным ключом.
То, что невозможно сделать с помощью вышеприведенного моделирования - это предотвращение того, что Place
является Restaurant
и Library
одновременно.