Django: Зачем создавать OneToOne для UserProfile вместо подкласса auth.User?

Примечание. Если у вас возникнет соблазн ответить на этот вопрос, сказав мне, что вам не нравится django.contrib.auth, пожалуйста, продолжайте. Это не поможет. Я хорошо осведомлен о разбросе и силе мнений по этому вопросу.

Теперь вопрос:

Соглашение заключается в создании модели UserProfile с OneToOne для пользователя.

Насколько я могу придумать, более эффективным и действенным подходом является создание подкласса User для класса, который предполагается использовать для каждого человека в системе, — класса, называемого, скажем, Person(User).

Я не видел вразумительного объяснения, почему первое считается обычным, а второе считается хаком. Некоторое время назад я перешел на подход OneToOne, чтобы получить возможность использовать get_profile(), и с тех пор жалею об этом. Я думаю вернуться назад, если меня не заставят понять преимущества этого подхода.


person jMyles    schedule 27.03.2011    source источник


Ответы (3)


Вы понимаете, не так ли, что подклассы модели реализованы посредством отношения OneToOne под капотом? На самом деле, что касается эффективности, я вообще не вижу никакой разницы между этими двумя методами.

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

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

person Daniel Roseman    schedule 27.03.2011
comment
Да, конечно, я понимаю, что создание подклассов создает неявный OneToOne. С точки зрения ясности и эффективности, person.email намного понятнее, чем userprofile.user.email. Можете ли вы конкретизировать, в чем проблема с созданием подклассов существующих моделей? Ваш третий вариант меня очень заинтересовал. Есть ли хороший документ, который я могу прочитать об этой технике? - person jMyles; 28.03.2011
comment
Кроме того, по поводу вашего второго абзаца: не можете ли вы сделать то же самое в отношении сокрытия попаданий в базу данных с помощью обычного OneToOne на конкретной модели? Или вы хотите сказать, что, поскольку вам не всегда нужен PK родителя, вы иногда можете сохранить поездку? Если да, то это достаточно справедливое замечание, но здесь оно на самом деле не применимо, поскольку мы всегда (по крайней мере, в каждом случае, который я могу придумать) все равно будем хотеть перейти к пользователю. - person jMyles; 28.03.2011
comment
когда я производил подкласс пользователя, это работало какое-то время, но позже, когда я создал многоточечное сквозное отношение, когда сквозной объект пытался получить доступ к FK к подклассированному модулю пользователя, вместо этого он фактически обращался к пользовательской модели, что вызывало все виды проблем и было невозможно исправить, не вникая во внутренности django. Это был джанго 1.3.1. Я переключился только на объекты профиля, которые указывают на некоторые пользовательские объекты, и это было решено. - person fastmultiplication; 23.03.2012
comment
Этот пост устарел? Я не понимаю, почему это неприятный хак, тем более, что Django теперь имеет контроль над настройкой пользовательской модели пользователя в настройках, и все, что хочет пользователь, должно получать его с помощью get_user_model() (не знаю, когда это было добавлено, хотя) - person Nick T; 18.02.2015
comment
Создание подклассов конкретной модели было и остается неприятным приемом. Создание подкласса абстрактной модели, как вы делаете, когда создаете подкласс AbstractUser для создания пользовательской модели пользователя, совершенно нормально, хотя это было недоступно, когда я писал этот пост. - person Daniel Roseman; 19.02.2015

На самом деле никогда не было хорошего объяснения, по крайней мере, из «официальных» источников, почему на практике создание подкласса User менее полезно, чем наличие UserProfile.

Тем не менее, у меня есть несколько причин, которые возникли после того, как я решил, что создание подкласса User - это "путь".

  • Вам нужен настраиваемый сервер аутентификации. Это не большая проблема, но чем меньше кода вам нужно написать, тем лучше.
  • Другие приложения могут предполагать, что ваш Пользователь является django.contrib.auth.models.User. В основном это будет нормально, если только этот код не извлекает объекты пользователя. Поскольку мы являемся подклассом, любой код, просто использующий наши объекты User, должен подойти.
  • Пользователь может «быть» только одним подклассом за раз. Например, если у вас есть подклассы User для Ученика и Учителя, то в определенный момент времени ваш Пользователь сможет быть только Учителем или Учеником. С UserProfiles к одному и тому же пользователю могут быть одновременно привязаны профиль учителя и ученика.
  • В дальнейшем преобразование из одного подкласса в другой затруднено, особенно если у вас уже есть экземпляр одного подкласса.

Итак, вы можете сказать: «Мой проект будет иметь только один подкласс User». Это то, о чем я думал. Теперь у нас трое плюс обычные пользователи и, возможно, четвертый. Требования меняются, необходимость изменять кучу кода, чтобы справиться с этим, не очень интересна.

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

person Matthew Schinckel    schedule 10.07.2012
comment
Если какие-либо другие приложения предполагают django.contrib.auth.models.User, их следует переписать, чтобы использовать get_user_model() - person Nick T; 06.10.2015

Является ли более эффективным и действенным наследование модели пользователя? Я не понимаю, почему, но я хотел бы прочитать ваши аргументы. ИМНШО, наследование моделей всегда было болью.

Тем не менее, это может не отвечать на ваш вопрос, но я вполне удовлетворен решением, предложенным Уиллом Харди в этом фрагменте< /а>. Используя сигналы, он автоматически создает новый профиль пользователя для каждого нового пользователя.

Ссылка вряд ли исчезнет, ​​но вот моя немного другая версия его кода:

from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _

class AuthUserProfileModelBase(models.base.ModelBase):
    # _prepare is not part of the public API and may change
    def _prepare(self):
        super(AuthUserProfileModelBase, self)._prepare()
        def on_save(sender, instance, created, **kwargs):
            if created:
                self.objects.create(user=instance)
        # Automatically link profile when a new user is created
        post_save.connect(on_save, sender=User, weak=False)

# Every profile model must inherit this class
class AuthUserProfileModel(models.Model):
    class Meta:
        abstract = True
    __metaclass__ = AuthUserProfileModelBase
    user = models.OneToOneField(User, db_column='auth_user_id',
        primary_key=True, parent_link=True)

# The actual profile model
class Profile(AuthUserProfileModel):
    class Meta:
        app_label = 'some_app_label'
        db_table = 'auth_user_profile'
        managed = True
    language = models.CharField(_('language'), max_length=5, default='en')

Конечно, все заслуги принадлежат Уиллу Харди.

person jweyrich    schedule 27.03.2011
comment
Одна из главных причин, по которой я считаю наследование благоприятным, по крайней мере теоретически, кратко выражена в П.С. во фрагменте Уилла: (PS: Было бы также неплохо иметь результирующий класс-прокси атрибутов объекта пользователя, как это делает наследование модели django, при этом автоматически создавая объект UserProfile при создании объекта пользователя :-) - person jMyles; 28.03.2011
comment
@Justin: я не вижу в этом реального преимущества, в основном потому, что прокси уже находится на расстоянии одного свойства - profile_instance.user.desired_attribute. - person jweyrich; 28.03.2011
comment
Хорошо, бывают случаи, когда я не знаю, есть ли у меня объект AfricanSwallow или Swallow, и я хочу иметь доступ к его кокосам в любом случае. Мне кажется непифоническим, что нужно определить это заранее - с этого и начинается весь смысл наследования классов. - person jMyles; 28.03.2011
comment
@Justin: я не вижу связи. На самом деле поставленный вопрос кажется довольно далеким от того, что мы здесь обсуждаем. Когда этот вопрос возник бы в рамках сценария вашего исходного вопроса? Извините, но я не понимаю. - person jweyrich; 28.03.2011
comment
Когда у меня есть родительский объект, и я хочу получить доступ к методу дочернего объекта. Статус дочернего объекта как члена подкласса важен, потому что я могу захотеть получить доступ к полям (или, если уж на то пошло), методам родителя. - person jMyles; 28.03.2011
comment
@Justin: Я понимаю это, но, по-моему, это не имеет никакого отношения к твоему первоначальному вопросу. И я полагаю, вы знаете это, потому что у вас уже есть это как другой вопрос. Вы столкнетесь с той же проблемой в любом случае, если вы ее решите, через наследование или нет. Я бы решил это, добавив свойство profile_type к AuthUserProfileModel. Таким образом, любой необработанный запрос также может различаться между ними. Сложность здесь в том, что Django позволяет настроить только один класс профиля. - person jweyrich; 28.03.2011
comment
Почему не СУХОЙ? AuthUserProfileModel — это базовый класс, поэтому вы можете поместить туда общие свойства/методы. - person jweyrich; 28.03.2011
comment
В сценарии Где авторитетное место, где можно посмотреть, что это за тип профиля? Есть два места: 1) неотъемлемое знание в объекте типа ob, которым он является, и 2) в profile_type. - person jMyles; 28.03.2011
comment
Я согласен с этим. Хотя ответ на ваш другой вопрос должен решить это. А пока мы должны принять то, как работает Python, или найти ответ самостоятельно. - person jweyrich; 28.03.2011