Introducción

Una de las ventajas de Django es su panel administrativo, donde (entre otras muchas cosas) puedes crear, modificar o borrar a los usuarios de tu sistema. Si estás manejando grupos de usuarios para restringir el acceso a ciertas áreas de tu aplicación, también desde este panel puedes gestionar los grupos y usuarios que pertenecen a cada uno estos. Tarde o temprano te encuentras en la necesidad de crear varios usuarios al mismo tiempo, por ejemplo, si tu sistema se usará en una escuela será necesario crear las cuentas para los maestros o alumnos, y hacerlo desde el admin uno por uno no solo es tedioso y muy tardado, también es propenso a muchos errores. En estos casos podemos usar la API del ORM de Django para automatizar la creación de los usuarios.

Requerimientos

Para este ejercicio vamos a necesitar dos grupos de usuarios:

  1. MAESTRO
  2. ALUMNO

Y un archivo CSV el cual tendrá la información de cada usuario a crear. La estructura de este archivo es la siguiente:

login nombre apellidos email grupo
aramirez Arturo Ramírez Tejeda [email protected] MAESTRO
flopez Francisco López García [email protected] ALUMNO

Relaciones entre Usuarios, Grupos y Correos

Django utiliza una relación muchos a muchos (many to many) para referenciar a los usuarios y grupos del sistema. En este tipo de relación se dice que un usuario pertenece a muchos grupos y un grupo lo tienen muchos usuarios. Y cada usuario está ligado a un email por medio de una relación uno a muchos, es decir un usuario puede tener varios emails, pero un email solo puede pertenecer a un usuario.

Para crear los usuarios y asignarlos a un grupo vamos a usar los modelos User, Group e EmailAddress, los cuales los importamos así:

from escuela.users.models import User
from django.contrib.auth.models import Group
from allauth.account.models import EmailAddress

Nota: en mi caso uso el paquete allauth, para que los usuarios puedan restablecer sus contraseñas por medio una liga que les llega a su correo, y para las aplicaciones donde los usuarios se pueden registrar por ellos mismos también permite la creación de usuarios por medio del email.

Implementación del código

Obteniendo los grupos

En nuestro caso ya sabemos de antemano los grupos que vamos a usar, por lo que podemos obtenerlos desde un inicio:

maestro = Group.objects.get(name="MAESTRO")
alumno = Group.objects.get(name="ALUMNO")

En este caso estoy obteniendo los grupos por su nombre y no por su id para hacerlo más descriptivo.

Abrimos el archivo para obtener los datos

import csv

# definimos unas constantes para referenciar los campos del archivo
LOGIN = 0
NOMBRE = 1
APELLIDOS = 2
EMAIL = 3
GRUPO = 4

with open("datos.csv", "r") as fp:
    reader = csv.reader(fp)
    for row in reader:
        # la creación de los usuarios va aquí 

Creación de los usuarios y grupos

El modelo para los usuarios tiene algunos campos requeridos que tenemos que crear junto con la información del archivos CSV, estos son:

  • is_superuser. Es un campo booleano, si es verdadero se considera un administrador del sistema.
  • is_staff. Otro campo booleano, en caso de ser verdadero el usuario puede iniciar sesión en el panel administrativo.
  • is_active. Campo booleano, indica si un usuario es activo o inactivo.
  • date_joined. Un campo de tipo datetime, el cual indica la fecha de creación del usuario.

En nuestro caso ningún usuario es superuser, pero los maestros sí pertenecen al staff. Todos los usuarios a crear serán activos. Primero tenemos que crear al usuario:

import datetime
hoy = datetime.datetime.today()

usuario = User(
    is_superuser=False
    is_staff=True if row[GRUPO] == "MAESTRO" else False,
    username=row[LOGIN],
    first_name=row[NOMBRE],
    last_name=row[APELLIDOS],
    email=row[EMAIL],
    is_active=True,
    date_joined=hoy
)

En este punto se ha creado es usuario pero aun no está guardado, pero necesitamos tener la instancia para la creación de la contraseña, la asignación del email y de los grupos.

Contraseñas

Cuando asignamos una contraseña desde el panel administrativo esta es validada por Django para que cumpla ciertas normas (longitud, complejidad), pero al crear la contraseña de forma programática podemos saltarnos estas validaciones. Esto es útil en caso de que nos soliciten igualar el nombre de usuario con la contraseña (lo cual dicho sea de paso no se debe de hacer). Para asignar una contraseña tenemos que usar el método set_password():

usuario.set_password("abracadabra")

En este caso nos estamos brincando cualquier validación de Django y asignamos el password tal cual lo especificamos. Podemos verificar si un password cumple los lineamientos de python con:

usuario.check_password("abracadabra") # En este caso regresa False

Por último, podemos generar un password aleatorio que cumple las normas de Django con el método make_random_password() de la clase BaseUserManager:

from django.contrib.auth import get_user_model

User = get_user_model()
my_pass = User.objects.make_random_password()
usuario.set_password(my_pass)

Grupos

Como ya explicamos en un inicio, los grupos y usuarios tienen una relación muchos a muchos, y para poder asignar esta relación, tanto el usuario como los grupos tienen que estar creados y guardados, por lo que tenemos que guardar nuestro usuario y asignarle el grupo correspondiente:

usuario.save()
usuario.groups.add(grupo)

Nota: cuando asignamos el grupo estamos usando groups, por que internamente el modelo User tiene definida la relación como groups = models.ManyToManyField(Group).

Al usar add(grupo) se guarda en automático la relación, por lo que no es necesario hacer un save().

Emails

El proceso de registro por email de allauth es:

  1. Se crea el usuario con un email (modelo User).
  2. Cuando el usuario inicia sesión por primera vez, se le envía un correo a la dirección registrada con una liga para autorizar su cuenta, y se crea un registro en el modelo EmailConfirmation, el cual indica que se está en espera de la validación por parte del usuario.
  3. Si el usuario valida su correo, se borra el registro de EmailConfirmation y se crea un nuevo registro en el modelo EmailAddress con la dirección confirmada.

En nuestro caso queremos omitir este proceso de validación para que el usuario (y su email) ya esté confirmado, por lo que solo tenemos que crear los registros en EmailAddress:

EmailAddress.objects.create(
    email=row[EMAIL],
    verified=True,
    primary=True,
    user=usuario
)

Nota: Para conocer las diferencias al crear una instancia con MiModelo() y MiModelo.objects.create() ve el post Primeros pasos con el ORM de Django.

Juntando todo

from django.contrib.auth.models import Group
from allauth.account.models import EmailAddress
from django.contrib.auth import get_user_model
import datetime
import csv

User = get_user_model()

LOGIN = 0
NOMBRE = 1
APELLIDOS = 2
EMAIL = 3
GRUPO = 4

hoy = datetime.datetime.today()

maestro = Group.objects.get(name="MAESTRO")
alumno = Group.objects.get(name="ALUMNO")

with open("datos.csv", "r") as fp:
    reader = csv.reader(fp)
    for row in reader:
        usuario = User(
            is_superuser=False
            is_staff=True if row[GRUPO] == "MAESTRO" else False,
            username=row[LOGIN],
            first_name=row[NOMBRE],
            last_name=row[APELLIDOS],
            email=row[EMAIL],
            is_active=True,
            date_joined=hoy
        )
        passwd = User.objects.make_random_password()
        usuario.set_password(passwd)
        usuario.save()
        # asignamos el grupo
        usuario.groups.add(maestro if row[GRUPO] == "MAESTRO" else alumno)
        # autorizamos el email
        EmailAddress.objects.create(
            email=row[EMAIL],
            verified=True,
            primary=True,
            user=usuario
        )

Nota: al inicio importaba el modelo User con from escuela.users.models import User pero aquí usé from django.contrib.auth import get_user_model, esto es por que yo le hice modificaciones a este modelo. Lo dejo así como referencia.

Este script lo podemos ejecutar con el shell de Django:

python manage.py shell < crear_usuarios.py

Conclusión

Django es un framework bastante completo y en la mayoría de los casos automatiza muchas cosas por nosotros. Sin embargo, nos da todas las herramientas para hacer cosas manuales si es que lo requerimos. El código final se puede mejorar agregando validaciones en el caso de que ya exista un usuario, o el archivo tenga un grupo inválido, pero esto ya se deja como ejercicio para el lector.