Реализация нескольких пользовательских типов с 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) Использование одной модели с атрибутом "тип"

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, как я могу отфильтровать своих пользователей?

Благодарю.

4 ответа

Решение

Предупреждение: 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('baseuser@users.com', password='baseuser')
>>> PrivateUser.objects.create_user('privateuser@users.com', password='privateuser', first_name='His', last_name='Name')
>>> TradeUser.objects.create_user('tradeuser@users.com', password='tradeuser', company_name='Tech Inc.')
>>> BaseUser.objects.all()
[<BaseUser: baseuser@users.com>, <BaseUser: privateuser@users.com>, <BaseUser: tradeuser@users.com>]
>>> PrivateUser.objects.all()
[<PrivateUser: privateuser@users.com>]
>>> TradeUser.objects.all()
[<TradeUser: tradeuser@users.com>]

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

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

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

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

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

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

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

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

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