tl; dr
Существует ли простая альтернатива многостоловому наследованию для реализации базового шаблона модели данных, изображенного ниже, в Django?
Предпосылка
Пожалуйстарассмотрим очень базовую модель модели данных на изображении ниже, основанную, например, на Hay, 1996 .
Проще говоря: Organizations
и Persons
равны Parties
, и все Parties
имеют Address
es.Подобный образец может применяться ко многим другим ситуациям.
Важным моментом здесь является то, что Address
имеет явное отношение с Party
, а не явные отношения с отдельными подмоделями Organization
и Person
.
![diagram showing basic data model](https://i.stack.imgur.com/bKF6V.png)
Обратите внимание, что каждая подмодель вводит дополнительные поля (здесь не показано, но см. Пример кода ниже).
Этот конкретный примеримеет несколько очевидных недостатков, но это не относится к делу.Ради этого обсуждения предположим, что шаблон прекрасно описывает то, чего мы хотим достичь, поэтому остается только вопрос: как реализовать шаблон в Django .
Реализация
Я полагаю, что наиболее очевидная реализация будет использовать multi-table-Наследование :
class Party(models.Model):
""" Note this is a concrete model, not an abstract one. """
name = models.CharField(max_length=20)
class Organization(Party):
"""
Note that a one-to-one relation 'party_ptr' is automatically added,
and this is used as the primary key (the actual table has no 'id'
column). The same holds for Person.
"""
type = models.CharField(max_length=20)
class Person(Party):
favorite_color = models.CharField(max_length=20)
class Address(models.Model):
"""
Note that, because Party is a concrete model, rather than an abstract
one, we can reference it directly in a foreign key.
Since the Person and Organization models have one-to-one relations
with Party which act as primary key, we can conveniently create
Address objects setting either party=party_instance,
party=organization_instance, or party=person_instance.
"""
party = models.ForeignKey(to=Party, on_delete=models.CASCADE)
Это, кажется, идеально соответствует шаблону.Это почти заставляет меня поверить, что именно для этого и предназначалось наследование нескольких таблиц.
Тем не менее, наследование нескольких таблиц выглядит как на , особенно из-за производительноститочка зрения, хотя зависит от приложения .Особенно этот страшный, но древний пост от одного из создателей Django весьма обескураживает:
Почти в каждом случае абстрактное наследование является лучшим подходом в долгосрочной перспективе.Я видел более чем несколько сайтов, разрушенных под нагрузкой, вызванной конкретным наследованием, поэтому я настоятельно рекомендую пользователям Django подходить к любому использованию конкретного наследования с большой долей скептицизма.
Несмотря на этоСтрашное предупреждение, я думаю, что главное в этом посте - следующее наблюдение относительно наследования нескольких таблиц:
Эти объединения имеют тенденцию быть «скрытыми» - они создаются автоматически - и это означает, что выглядиткак простые запросы, часто это не так.
Устранение неоднозначности : В вышеприведенном посте Django ссылается на «наследование нескольких таблиц» как «конкретное наследование», которое не следует путать с Наследование бетонных таблиц на уровне базы данных.Последнее на самом деле лучше соответствует понятию наследования в Django с использованием абстрактных базовых классов.
Я думаю, этот вопрос SO хорошо иллюстрирует проблему "скрытых объединений".
Альтернативы
Абстрактное наследование не кажется мне жизнеспособной альтернативой, потому что мы не можем установить внешний ключ для абстрактной модели, что имеет смысл, потому что у него нет таблицы.Я предполагаю, что это подразумевает, что нам понадобится внешний ключ для каждой «дочерней» модели плюс некоторая дополнительная логика для имитации этого.
Наследование прокси также не кажется опцией, так как каждая из подмоделей вводит дополнительные поля, EDIT: Если подумать, прокси-модели могут быть вариантом, если мы используем Single Table Inheritance на уровне базы данных, т.е. используем одну таблицу, которая включает в себя всеполя из Party
, Organization
и Person
.
GenericForeignKey могут быть опцией в некоторых особых случаях , но для меня они - материалночных кошмаров.
В качестве другой альтернативы часто предлагается использовать явные взаимно-однозначные отношения (здесь кратко eoto ) вместо наследования нескольких таблиц (так что Party
, Person
и Organization
будут просто подклассами models.Model
).
Обаpproaches, наследование нескольких таблиц ( mti ) и явные отношения один к одному ( eoto ) приводят к трем таблицам базы данных.Таким образом, в зависимости от типа запроса, конечно, , некоторая форма JOIN
часто неизбежна при получении данных.
Изучив результирующие таблицы в базе данных, становится ясно, что единственное различие между mti и eoto на уровне базы данных состоит в том, что eoto Person
таблица имеет столбец id
в качестве первичного ключа и отдельный столбец внешнего ключа для Party.id
, тогда как таблица mti Person
имеет нет отдельный id
столбец, но вместо этого используется внешний ключ к Party.id
в качестве первичного ключа.
Вопрос (ы)
Я не думаю, что поведениеиз примера (особенно одного прямого отношения к родителю) можно добиться с помощью абстрактного наследования , не так ли?Если это может , то как бы вы этого достигли?
Действительно ли явное отношение один к одному действительно намного лучше, чем наследование нескольких таблиц, за исключением того факта, что оно вызываетнам сделать наши запросы более явными?Для меня удобство и ясность многостолового подхода перевешивает аргумент явности.
Примечание , что этот вопрос SO очень похож, но не совсем отвечает на мойвопросы.Более того, последний ответ существует уже почти девять лет , и с тех пор Django сильно изменился.
[1]: Hay 1996, Шаблоны моделей данных