Многостабильное наследование Django ПРОТИВ Указания Явного отношения OneToOne в Моделях

StackOverflow https://stackoverflow.com/questions/1796556

  •  22-09-2019
  •  | 
  •  

Вопрос

Надеюсь, все это имеет смысл :) При необходимости я уточню в комментариях.Кроме того, я экспериментирую с использованием выделенного жирным шрифтом текста в этом вопросе и отредактирую его, если я (или вы) сочтете это отвлекающим.С этим покончено...

Использование django.contrib.auth дает нам пользователя и группу, среди прочих полезных вещей, без которых я не могу обойтись (например, базовый обмен сообщениями).

В моем приложении у меня есть несколько разных типов пользователей.Пользователь может быть только одного типа.С этим легко справились бы группы, проявив немного дополнительной осторожности. Однако эти разные пользователи связаны друг с другом иерархиями / отношениями.

Давайте взглянем на этих пользователей:-

Руководители - пользователи "верхнего уровня"

Администраторы - каждый администратор подчиняется Участнику

Координаторы - каждый координатор подчиняется Администратору

Помимо этих существуют и другие типы пользователей, которые не связаны напрямую, но могут быть связаны позже.Например, "Компания" - это другой тип пользователей, и у нее могут быть различные "Продукты", и продукты могут контролироваться "Координатором"."Покупатель" - это другой тип пользователей, которые могут покупать товары.

Теперь все эти пользователи имеют различные другие атрибуты, некоторые из которых являются общими для всех типов пользователей, а некоторые отличаются только для одного типа пользователей.Например, все типы пользователей должны иметь адрес.С другой стороны, только Основной пользователь принадлежит к "Филиалу".

Другой момент, о котором было сказано выше, заключается в том, что пользователь может принадлежать только к одному типу.

Приложение также необходимо отслеживать, кто создал и / или изменил Руководителей, Администраторов, Координаторов, Компании, Продукты и т. Д.(Итак, это еще две ссылки на пользовательскую модель.)

В этом сценарии, является ли хорошей идеей использовать многостоловое наследование 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 использует отношение "один к одному" для наследования нескольких таблиц за кулисами.Я просто недостаточно квалифицирован, чтобы решить, какой подход является более разумным.

Это было полезно?

Решение

Я хотел бы подробнее остановиться на решении @thornomad.

Прямое расширение пользовательского класса Django может вызвать всевозможные проблемы с внутренним django.механизмы аутентификации.То, что я сделал в подобной ситуации, - это именно то, что предлагает @thornomad - я создал свою собственную модель UserProfile, связанную один к одному с пользовательской моделью Django, в которой я хранил дополнительные пользовательские данные и из которой я унаследовал модели для разных типов пользователей.

Что-то, соответствующее тому, что вы описали:

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)

Другие советы

Недавно я перешел на использование моделей, которые наследуются от contrib.auto.models.User.Мое общее наблюдение таково, что теоретически они великолепны, но иногда с ними не происходит автоматической магической обработки так, как предполагалось.

Я думаю, что ваше решение относительно наследования противOneToOne сводится к этому:

  • Хочу ли я, чтобы Django автоматически делал что-то правильно в 95% случаев, и мне нужно отлаживать эти остальные 5%

-ИЛИ-

  • Хочу ли я сам что-то делать вручную в 100% случаев

Если вы этого не видели, в блоге Скотта Бархэма есть отличный пост о наследовании User, а также о создании пользовательского интерфейса, чтобы убедиться, что ваш пользовательский объект возвращается -- Расширение пользователя Django.

Дополнительно представляет интерес поле AutoOneToOne, предоставленное джанго-раздражающий.Здесь это своего рода гибрид двух подходов - наследования не происходит, но Django заботится о создании соответствующего OneToOneField, если его нет.

Также, торномад действительно, это хороший аргумент по поводу избыточности в ваших моделях.Вы могли бы легко реализовать абстрактный класс, чтобы очистить это таким образом (предполагая, что вы выполняете 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)

Я не думаю, что я унаследовал бы User модель, скорее используйте пользовательскую UserProfile - покидая contrib.auth модель одна.С обычаем UserProfile модель, вы могли бы настроить База модель профиля пользователя, которая может быть частью всех ваших различных типов пользователей.

Просто взглянув на это быстро, я бы также внимательно посмотрел на любые модели, которые повторяют все те же поля (например, ваши последние два Principle и Administrator модели).Сочетание встроенной функциональности группы с идеей профиля пользователя может дать то, что вы ищете.

Пожалуйста, подумайте, что происходит в модели данных, когда Координатор получает повышение до Руководителя.Я бы вообще не стал использовать наследование в этом случае.Пожалуйста, пересмотрите предложение предыдущего плаката "Объединение встроенной функциональности группы с идеей пользовательского профиля может дать то, что вы ищете".

Нужны ли вам объекты ваших пользовательских классов, чтобы действовать как auth.Пользователь где угодно?Это было бы самой очевидной причиной использовать наследование поверх OneToOne .Одним из преимуществ подхода OneToOne является легкость, с которой вы можете перейти на другую пользовательскую модель, если это вас беспокоит.

Реальная проблема, которую я вижу в том, что у вас есть выше (любым методом), заключается в том, что, похоже, ничто не мешает вам использовать основной объект и объект администратора у одного и того же пользователя.OneToOneField может гарантировать только взаимно однозначное сопоставление между любыми двумя отношениями.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top