Implementación de múltiples tipos de usuario con Django 1.5
django-models django-1.5 (4)
¿Cuál es la forma recomendada de implementar múltiples tipos de usuarios utilizando la nueva funcionalidad de modelo de usuario configurable de Django 1.5?
Me gustaría tener dos tipos de usuarios: usuarios privados y usuarios comerciales, cada uno con su propio conjunto de campos obligatorios.
Hay dos maneras en que puedo pensar para implementar esto:
1) herencia de mesas múltiples
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)
# ...
¿Hay algún problema con el uso de la herencia de tablas múltiples junto con el modelo de usuario configurable?
2) Usar un solo modelo con un atributo de "tipo"
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)
# ...
Este método requeriría una validación condicional que depende de user_type
.
¿Cuál de estos métodos se adapta mejor a mi caso de uso? ¿O tal vez hay una mejor manera de lograr esto?
Además, en el caso número 1, ¿cómo puedo filtrar a mis usuarios?
Gracias.
¿Tal vez deberías considerar AbstractUser?
Advertencia: Django 1.5 es muy nuevo y la gente todavía está investigando sus nuevas características. Entonces mi respuesta no es más que mi opinión, basada en investigaciones recientes para responder a esta pregunta.
Ambas formas son formas válidas para lograr el resultado, con sus ventajas y desventajas.
Comencemos con el:
Segunda opción
- Sin modelos anidados y no modulares.
AbstractBaseUser
, como su nombre lo dice, es un modelo abstracto y no tiene una tabla específica - Tiene campos sin usar
Debe verificar el user_type para cualquier iteración con el modelo que usa los campos adicionales:
def foo(): if user.user_type == ''Private'': # ... else: # ...
El SQL resultante sería aproximadamente de la siguiente manera:
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
);
Primera opción
- Modelos anidados con separación lógica de entidades
- Muy delgado
- Debe implementar
BaseUserManager
para cada elemento secundario si desea usar las funcionescreate_user
- No puede acceder a las subclases con un simple
BaseUser.objects.all()
*
El SQL resultante sería aproximadamente de la siguiente manera:
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
);
* Imagine la siguiente situación:
>>> 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]>]
Por lo tanto, no puede recuperar directamente las instancias de subclases utilizando BaseUser.objects.all()
. Hay una excelente publicación en el blog de Jeff explicando mejor cómo lograr un " BaseUser
automático" de BaseUser
a sus hijos.
Dicho esto, debe considerar las ventajas y desventajas de cada enfoque y su impacto en su proyecto. Cuando la lógica involucrada es pequeña (como en el ejemplo descrito), ambos enfoques son válidos. Pero en un escenario más complejo, un enfoque puede ser mejor que el otro. Elegiría la opción de modelo múltiple porque es más extensible.
El nuevo modelo de usuario personalizado puede asignar solo un modelo a AUTH_USER_MODEL. Con la herencia de tablas múltiples tienes dos modelos. Entonces eso es un problema.
En el caso de un modelo de usuario único que cubre ambos tipos de usuario, puede abstraer la lógica condicional en los métodos del modelo. También puede usar diferentes administradores para diferentes tipos de usuarios en función de cuánto son diferentes. Esto también podría ayudarlo a ser explícito cuando trabaje con un tipo de usuario específico.
Otra opción podría ser almacenar solo los atributos más comunes en un único modelo de usuario y luego adjuntar detalles de dos tipos de usuarios en sus propias tablas que están vinculadas a su tabla principal de usuarios.
Si ambos usuarios tienen la mayoría de las cosas en común (en términos de datos al menos), los mantendría en un solo lugar. Al final, pensaría en lo que es más simple y más fácil de mantener.
Usaría un único modelo con un atributo "tipo". Aquí es por qué:
- Solo una mesa, solo un modelo
- Si quiere convertir de un tipo a otro, simplemente cambie el atributo
- Implementar getters y setters para los campos que existen para un tipo y no existe para otro = mucho más simple de usar.