Подтвердить что ты не робот

Реализация нескольких типов пользователей с помощью Django 1.5

Каков рекомендуемый способ реализации нескольких пользовательских типов с использованием новой функциональной возможности настраиваемой пользовательской модели Django 1.5?

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

Есть два способа, которые я могу реализовать, чтобы реализовать это:

1) Наследование с несколькими таблицами

class BaseUser(AbstractBaseUser):
  email = models.EmailField(max_length=254, unique=True)
  # ...


class PrivateUser(BaseUser):
  first_name = models.CharField(max_length=30)
  last_name = models.CharField(max_length=30)
  # ...


class TradeUser(BaseUser):
  company_name = models.CharField(max_length=100)
  # ...

Есть ли проблемы с использованием наследования нескольких таблиц в сочетании с настраиваемой пользовательской моделью?

2) Использование одной модели с атрибутом "type"

class User(AbstractBaseUser):
  email = models.EmailField(max_length=254, unique=True)
  user_type = models.CharField(max_length=30, choices={
    'P': 'Private',
    'T': 'Trade',
  })
  first_name = models.CharField(max_length=30, blank=True)
  last_name = models.CharField(max_length=30, blank=True)
  company_name = models.CharField(max_length=100, blank=True)
  # ...

Этот метод потребует некоторой условной проверки, зависящей от user_type.

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

Также, в случае номер 1, как я могу фильтровать своих пользователей?

Спасибо.

4b9b3361

Ответ 1

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

Оба способа - это допустимые способы достижения результата с его преимуществами и недостатками.

Начнем с:

Второй вариант

  • Без вложенных моделей, а не модульных. AbstractBaseUser, как сказано в названии, является абстрактной моделью и не имеет конкретной таблицы
  • Неиспользуемые поля
  • Вам нужно проверить user_type для любой итерации с моделью, которая использует дополнительные поля:

    def foo():
        if user.user_type == 'Private':
            # ...
        else:
            # ...
    

Результирующий SQL будет примерно следующим:

CREATE TABLE "myapp_user" (
    "id" integer NOT NULL PRIMARY KEY,
    "password" varchar(128) NOT NULL,
    "last_login" datetime NOT NULL,
    "email" varchar(254) NOT NULL UNIQUE,
    "user_type" varchar(30) NOT NULL,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL,
    "company_name" varchar(100) NOT NULL
);

Первая опция

  • Вложенные модели с логическим разделением объектов
  • Очень скудный
  • Вы должны реализовать BaseUserManager для каждого дочернего , если вы хотите использовать create_user-подобные функции
  • Вы не можете получить доступ к подклассам с помощью простого BaseUser.objects.all() *

Результирующий SQL будет примерно следующим:

CREATE TABLE "myapp_baseuser" (
    "id" integer NOT NULL PRIMARY KEY,
    "password" varchar(128) NOT NULL,
    "last_login" datetime NOT NULL,
    "email" varchar(254) NOT NULL UNIQUE
);

CREATE TABLE "myapp_privateuser" (
    "baseuser_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_baseuser" ("id"),
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

CREATE TABLE "myapp_tradeuser" (
    "baseuser_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_baseuser" ("id"),
    "company_name" varchar(100) NOT NULL
);

* Представьте себе следующую ситуацию:

>>> BaseUser.objects.create_user('[email protected]', password='baseuser')
>>> PrivateUser.objects.create_user('[email protected]', password='privateuser', first_name='His', last_name='Name')
>>> TradeUser.objects.create_user('[email protected]', password='tradeuser', company_name='Tech Inc.')
>>> BaseUser.objects.all()
[<BaseUser: [email protected]>, <BaseUser: [email protected]>, <BaseUser: [email protected]>]
>>> PrivateUser.objects.all()
[<PrivateUser: [email protected]>]
>>> TradeUser.objects.all()
[<TradeUser: [email protected]>]

Таким образом, вы не можете напрямую извлекать экземпляры подклассов с помощью BaseUser.objects.all(). Существует отличный отличный пост в блоге Джеффом, объясняющим, как выполнить "автоматическое понижение" от BaseUser до его дочерних элементов.

Тем не менее, вы должны учитывать преимущества и недостатки каждого подхода и их влияние на ваш проект. Когда вовлеченная логика мала (как в описанном примере), оба подхода действительны. Но в более сложном сценарии подход может быть лучше, чем другой. Я бы выбрал вариант с несколькими моделями, потому что он более расширяемый.

Ответ 2

Может быть, вы должны рассмотреть AbstractUser?

Ответ 3

В новой пользовательской модели вы можете назначить только одну модель AUTH_USER_MODEL. При наложении нескольких таблиц у вас есть две модели. Так что это проблема.

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

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

Если у обоих пользователей есть что-то общее (по крайней мере, по крайней мере), я бы сохранил их в одном месте. В конце концов, я бы подумал о том, что проще и проще в обслуживании.

Ответ 4

Я бы использовал одну модель с атрибутом типа. Вот почему:

  • Только одна таблица, только одна модель
  • Если вы хотите конвертировать из одного типа в другой, вы просто изменяете атрибут
  • Реализовать получатели и сеттеры для полей, которые существуют для одного типа и не существует для других, - гораздо проще в использовании.