Реализация нескольких пользовательских типов с 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
своим детям.
Тем не менее, вы должны учитывать преимущества и недостатки каждого подхода и их влияние на ваш проект. Когда задействованная логика мала (как в описанном примере), оба подхода верны. Но в более сложном сценарии подход может быть лучше, чем другой. Я бы выбрал мультимодельный вариант, потому что он более расширяемый.
В новой пользовательской модели пользователя вы можете назначить только одну модель для AUTH_USER_MODEL. С наследованием нескольких таблиц у вас есть две модели. Так что это проблема.
В случае однопользовательской модели, которая охватывает оба пользовательских типа, вы можете абстрагировать условную логику в методах модели. Вы также можете использовать разные менеджеры для разных типов пользователей в зависимости от того, насколько они разные. Это также может помочь вам быть явным при работе с конкретным типом пользователя.
Другим вариантом может быть сохранение только наиболее распространенных атрибутов в однопользовательской модели, а затем прикрепление спецификаций двух пользовательских типов в их собственные таблицы, которые связаны с вашей основной пользовательской таблицей.
Если бы у обоих пользователей было много общего (по крайней мере, с точки зрения данных), я бы держал их в одном месте. В конце концов, я бы подумал о том, что проще и легче поддерживать.
Я бы использовал одну модель с атрибутом типа. Вот почему:
- Только одна таблица, только одна модель
- Если вы хотите конвертировать из одного типа в другой, просто измените атрибут
- Реализуйте методы получения и установки для полей, которые существуют для одного типа и не существуют для других = гораздо проще в использовании.