Настройка двух разных типов пользователей в Django 1.5/1.6

Обратите внимание: это обновленная версия мой первоначальный вопрос на эту тему, но заслуживает того, чтобы задать его еще раз в связи с изменением того, как Django работает с пользователями и аутентификацией.

Я работаю над веб-сайтом с двумя очень разными пользователями — назовем их Customers и Store Owners. Оба регистрируются на сайте, но имеют очень разный функционал. Customers просто имеют один профиль и могут делать покупки в тех магазинах, которые им нравятся. Store Owners имеют одну учетную запись, но могут иметь доступ к нескольким магазинам, и каждый магазин может иметь несколько Store Owners.

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

Customer
  email (username)
  password
  name
  address
  time_zone
  preferred_shipping
  favorite_stores (many-to-many field)
  ...

Store Owner
  email (username)
  password
  name
  balance
  stores_owned (many-to-many field on Stores)
  stores_managed (many-to-many field on Stores)
  ...

Первоначально, когда в Django была плохая пользовательская поддержка, у меня был класс UserProfile с некоторыми дополнительными полями с OneToOne на User, а затем дополнительные классы Customer и StoreOwner, которые были OneToOne на UserProfile. Это не сработало.

Учитывая изменения в Django 1.5/1.6, я пытаюсь придумать лучший способ структурировать это. Прямо сейчас у меня есть следующее:

class CustomerUser(AbstractBaseUser):
    ...

class StoreOwnerUser(AbstractBaseUser):
    ...

Но поскольку будет два типа пользователей, я не могу установить AUTH_USER_MODEL только для одного из них.

Каков наилучший способ структурировать это, чтобы у меня было два разных типа пользователей с разными полями, не вызывая проблем с аутентификацией пользователя, созданием пользователя или администрированием?

Кроме того, как я смогу определить только по входу в систему, является ли этот пользователь CustomerUser или StoreOwnerUser?


person jdotjdot    schedule 26.12.2013    source источник
comment
Вы можете создать базового пользователя, добавить логический флаг, указывающий, является ли пользователь покупателем или владельцем магазина, и прикрепить другую модель, которая является профилем, используя отношение «один к одному».   -  person user1876508    schedule 26.12.2013
comment
Прошел почти год с тех пор, как об этом спросили, но я нахожусь в похожей ситуации. Вопрос: какой подход вы в итоге выбрали? Кроме того, рассматривали ли вы случай, когда владелец магазина может захотеть зарегистрироваться в качестве покупателя (используя тот же адрес электронной почты)? Как ваш дизайн подходит для этого случая? Наконец, задумывались ли вы когда-нибудь о том, чтобы иметь отдельные представления для входа в систему для владельцев магазинов и покупателей (что может помочь легко отличить тип пользователя, выполняющего вход)?   -  person Hari Mahadevan    schedule 25.10.2014


Ответы (3)


Кажется, что у ваших типов пользователей есть некоторые общие и необычные функции. Если в ваших типах пользователей есть общие функции, которые стандартная модель пользователя Django не поддерживает из коробки, вам следует создать подкласс напрямую.

Добавление дополнительных, необычных функций к вашим типам пользователей лучше всего делать не путем создания подклассов, а с помощью профиля. Мое обоснование этого заключается в том, что ваша аутентификация для этих типов пользователей принципиально не меняется, но детали о пользователе меняются в зависимости от типа пользователя. Для этого вы создаете отдельную модель с этими данными и ссылаетесь на свой класс User как на отношение OneToOne/ForeignKey (в зависимости от вашего дизайна).

Вы можете внести изменения в процесс создания пользователя, чтобы определить, каким типом пользователя он должен быть, и установить для связанного с ним OneToOneField/ForeignKey (в зависимости от вашего проекта) соответствующую модель типа клиента.

Делая это таким образом, у вас должна быть только одна AUTH_USER_MODEL, и вы должны иметь возможность обрабатывать данные для разных типов клиентов.

person tsurantino    schedule 26.12.2013
comment
Спасибо - это имеет смысл. Но как я могу узнать из входа в систему, является ли вход пользователя StoreOwner или Customer? Будет ли в базовом классе UserProfile поле storeowner_or_customer или что-то в этом роде? - person jdotjdot; 26.12.2013
comment
Что-то такое. UserProfile должен быть доступен Пользователю, и вы будете устанавливать его каждый раз при создании Пользователя. Я бы посоветовал вам иметь две модели профиля в качестве двух ваших типов пользователей (StoreOwner/Customer), а затем вы можете добавить поле типа в свою модель пользователя, на которую ссылаются две модели типов пользователей через отношение OneToOne. В своем коде вы сначала проверяете тип (т. е. user_obj.type == Customer или что-то в этом роде), и если это правда, вы можете получить доступ к соответствующим деталям напрямую user_obj.customer.address. - person tsurantino; 26.12.2013

Каков наилучший способ структурировать это, чтобы у меня было два разных типа пользователей с разными полями, не вызывая у меня никаких проблем с аутентификацией пользователя, созданием пользователя или администратором?

На самом деле у вас есть только один тип пользователей. Просто у некоторых пользователей установлены определенные свойства, а у других нет. Рассмотрим, как в django есть «пользователи» и «администраторы». Это экземпляры одной и той же модели, но с разными свойствами и разрешениями.

Вы должны подойти к этому так же. Используйте одну пользовательскую модель для всего приложения. Вы можете установить свойства/методы в своем пользовательском классе, чтобы определить, какие флаги установил этот пользователь (что будет определять «тип» пользователя).

Кроме того, как я смогу определить только по входу в систему, является ли этот пользователь CustomerUser или StoreOwnerUser?

Вы можете использовать user_passes_test декоратор, который принимает аргумент, являющийся именем функции, и обрабатывает представление только в том случае, если функция возвращает истинное значение.

person Burhan Khalid    schedule 26.12.2013
comment
Мне очень нравится эта идея, но не приведет ли это к тому, что у меня будет большое количество неиспользуемых полей в модели? Есть ли какие-то последствия, особенно для генерации форм? - person jdotjdot; 26.12.2013
comment
Нет, особенно если вы правильно используете обратные связи моделей. Например, вам не нужны поля stores_owned и stores_managed, потому что эту информацию вы можете получить с помощью ORM. Например, у вас может быть метод в User, который будет возвращать self.stores_set.count() для управляемых магазинов; у вас был бы этот метод в модели, если бы вы отображали его в панели администратора как часть представления списка моделей, иначе это было бы значение, которое было бы где-то в кеше. - person Burhan Khalid; 26.12.2013

  1. Создайте BaseUser, который расширяет абстрактного базового пользователя Django.
  2. Создайте два подкласса с именами CustomerUser и StoreOwnerUser, которые расширяют BaseUser.

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    
    class BaseUser(AbstractUser):
        # all the common fields go here, for example:
        email = models.EmailField(max_length=10,unique=True)
        name = models.CharField(max_length=120)
    
    class StoreOwnerUser(BaseUser):
        # All Store Owner specific attribute goes here
        balance = models.some_balance_field()
        stores_owned = models.some_stores_owned_field()
    
        class Meta:
        verbose_name = 'Store Owner'
    
    class CustomerUser(BaseUser):
        # All Customer specific attribute goes here
        customer_id = models.CharField(max_length=30, unique=True)
        address =  models.some_address
        time_zone = models.something...
        ...
    
        class Meta:
            verbose_name = 'Customer'
    
person Kashif Siddiqui    schedule 07.08.2015
comment
Что будет указано как AUTH_USER_MODEL? Также во время входа в систему: user = authenticate(username = username, password = password). Из какой таблицы (CustomerUser или StoreOwnerUser) будет выполняться проверка функции authenticate? - person Sibtain; 29.10.2015
comment
вы можете сделать эту реализацию stackoverflow. ком/вопросы/30495979/ - person Saber Solooki; 04.01.2017