Django Multi-Table Inheritance VS Определение явных отношений OneToOne в моделях
Надеюсь, что все это имеет смысл:) Я уточню, если это необходимо, в комментариях. Кроме того, я экспериментирую с использованием жирного текста в этом вопросе и отредактирую его, если я (или вы) нахожу его отвлекающим. С этим с дороги...
Использование django.contrib.auth дает нам User и Group, среди других полезных вещей, с которыми я не могу обойтись (например, базовые сообщения).
В моем приложении у меня есть несколько разных типов пользователей. Пользователь может иметь только один тип. Это будет легко обрабатываться группами с небольшой осторожностью. Однако эти разные пользователи связаны друг с другом в иерархиях/отношениях.
Посмотрите на этих пользователей: -
Принципалы - пользователи верхнего уровня
Администраторы - каждый администратор сообщает Принципалу
Координаторы - каждый координатор отчитывается перед Администратором
Помимо этих существуют другие типы пользователей, которые напрямую не связаны с, но могут быть связаны позже. Например, "Компания" является другим типом пользователя и может иметь различные "Продукты", а продукты могут контролироваться "Координатором". "Покупатель" - это другой вид пользователя, который может покупать продукты.
Теперь все эти пользователи имеют различные другие атрибуты, некоторые из которых являются общими для всех типов пользователей, а некоторые из них отличаются только одним типом пользователя. Например, все типы пользователей должны иметь адрес. С другой стороны, только основной пользователь принадлежит к "BranchOffice".
Еще одна точка, о которой говорилось выше, заключается в том, что Пользователь может быть только одного типа.
Приложение также должно отслеживать, кто создал и/или изменил Принципов, Администраторов, Координаторов, Компаний, Продуктов и т.д.. (Так что еще две ссылки на модель пользователя.)
В этом случае целесообразно использовать наследование нескольких таблиц Django следующим образом: -
from django.contrib.auth.models import User
class Principal(User):
#
#
#
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Или мне нужно делать это так: -
class Principal(models.Model):
#
#
#
user = models.OneToOneField(User, blank=True)
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Пожалуйста, имейте в виду, что существуют другие типы пользователей, связанные с внешними ключами, например: -
class Administrator(models.Model):
#
#
#
principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
user = models.OneToOneField(User, blank=True)
province = models.ForeignKey( Province)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")
Мне известно, что Django использует отношения "один-к-одному" для наследования нескольких таблиц за кулисами. Я просто недостаточно квалифицирован, чтобы решить, какой из них более разумный.
Ответы
Ответ 1
Я хочу расширить решение от @thornomad.
Расширение класса Django User напрямую может вызвать всевозможные проблемы с внутренними механизмами django.auth. То, что я сделал в подобной ситуации, именно то, что предлагает @thornomad, - я создал мою собственную модель UserProfile, связанную один к одному с моделью Django User, в которой я содержал дополнительные пользовательские данные и из которых я унаследовал модели для разных типов пользователей.
Что-то, что вам понравилось:
class UserProfile(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
class Meta:
abstract = True
class PositionHolderUserProfile(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")
class Principal(PositionHolderUserProfile):
branchoffice = models.ForeignKey(BranchOffice)
class Administrator(PositionHolderUserProfile):
superior = models.ForeignKey(Principal, related_name="subordinates")
province = models.ForeignKey(Province)
class Coordinator(PositionHolderUserProfile):
superior = models.ForeignKey(Administrator, related_name="subordinates")
class Company(UserProfile):
name = models.CharField(max_length=50)
class Product(models.Model):
name = models.CharField(max_length=50)
produced_by = models.ForeignKey(Company)
class Buyer(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
products_bought = models.ManyToManyField(Product)
Ответ 2
Недавно я переключился на использование моделей, которые наследуют от contrib.auto.models.User. Мое общее замечание состоит в том, что в теории они замечательные, но иногда они не получают автоматическое магическое обращение, как это предполагалось.
Я думаю, ваше решение относительно наследования против OneToOne сводится к следующему:
- Я хочу, чтобы Django автоматически выполнял что-то правильно в 95% случаев, и нужно отлаживать другие 5%
-OR -
- Я хочу сделать что-то вручную самостоятельно в 100% случаев
Если вы этого не заметили, в блоге Скотта Бархама есть отличный пост о наследовании пользователя, а также создание пользовательского конца, чтобы убедиться, что ваш пользовательский объект возвращается - Расширение пользователя Django.
Кроме того, интерес будет поле AutoOneToOne, предоставленное django-annoying. Это своего рода гибрид двух подходов здесь - нет наследования, но Django заботится о создании соответствующего OneToOneField, если он не присутствует.
Кроме того, thornomad делает хороший вывод об избыточности в ваших моделях. Вы можете легко реализовать абстрактный класс, чтобы очистить его так (при условии, что вы выполняете ручной OneToOne):
class BaseExtendedUser(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")
class Meta:
abstract = True
class Administrator(BaseExtendedUser):
province = models.ForeignKey(Province)
class Principal(BaseExtendedUser):
branchoffice = models.ForeignKey(BranchOffice)
Ответ 3
Я не думаю, что наследовал бы модель User
, а использовал бы пользовательский UserProfile
- оставив только модель contrib.auth
. С помощью пользовательской модели UserProfile
вы можете настроить базовую модель профиля пользователя, которая может быть частью всех ваших разных типов пользователей.
Просто взглянув на это быстро, я бы внимательно посмотрел на любые модели, которые повторяют все те же поля (например, ваши последние две модели Principle
и Administrator
). Сочетание встроенных функциональных возможностей группы с идеей профиля пользователя может делать то, что вы ищете.
Ответ 4
Пожалуйста, подумайте о том, что происходит в модели данных, когда Координатор получает повышение до Принципала. В этом случае я бы не использовал наследование. Пожалуйста, пересмотреть предыдущее предложение плаката "Объединение встроенных функциональных возможностей группы с идеей профиля пользователя может делать то, что вы ищете".
Ответ 5
Вам нужны объекты ваших пользовательских классов, чтобы действовать как auth.User где угодно? Это была бы самая очевидная причина использования наследования над OneToOne. Один из подходов OneToOne - это легкость, с которой вы можете перейти к другой модели пользователя, если это вызывает беспокойство.
Реальная проблема, которую я вижу с тем, что у вас выше (с помощью любого метода), заключается в том, что, похоже, нет ничего, что мешает вам иметь объект Principal, а объект Administrator имеет один и тот же пользователь. OneToOneField может гарантировать взаимно однозначное отображение между любыми двумя отношениями.