La autenticación que viene con Django es suficiente para la mayoría de los casos comunes, pero puede que tengas necesidades no cubiertas por las configuraciones predeterminadas. Personalizar la autenticación en tus proyectos requiere entender qué puntos del sistema proporcionado son extensibles o reemplazables. Este documento proporciona detalles sobre cómo se puede personalizar el sistema de autenticación.
Backend de autenticación proporcionan un sistema extensible para cuando un nombre de usuario y contraseña almacenados con el modelo de usuario necesitan ser autenticados contra un servicio diferente al predeterminado de Django.
Puedes dar a tus modelos permisos personalizados que se puedan comprobar a través del sistema de autorización de Django.
Puedes extender el modelo User predeterminado, o sustituir un modelo completamente personalizado.
Es posible que necesites conectar con otra fuente de autenticación – es decir, otra fuente de nombres de usuario y contraseñas o métodos de autenticación.
Por ejemplo, tu empresa puede tener ya configurado un LDAP que almacena un nombre de usuario y contraseña para cada empleado. Sería un problema tanto para el administrador del sistema como para los usuarios si estos últimos tuvieran cuentas separadas en LDAP y en las aplicaciones basadas en Django.
Para manejar situaciones como esta, el sistema de autenticación de Django te permite conectar otras fuentes de autenticación. Puedes sobrescribir el esquema por defecto de la base de datos de Django o utilizar el sistema por defecto junto con otros sistemas.
Consulte la referencia del backend de autenticación para obtener información sobre los backends de autenticación incluidos en Django.
Detrás de escena, Django mantiene una lista de «backends de autenticación» que comprueba la autenticación. Cuando alguien llama a django.contrib.auth.authenticate() – como se describe en Cómo loguear a un usuario – Django intenta autenticar a través de todos sus backends de autenticación. Si el primer método de autenticación falla, Django intenta el segundo, y así sucesivamente, hasta que se hayan intentado todos los backends.
La lista de backends de autenticación a utilizar se especifica en la AUTHENTICATION_BACKENDS configuración. Esto debe ser una lista de nombres de ruta Python que apuntan a clases de Python que saben cómo autenticar. Estas clases pueden estar en cualquier parte de tu ruta de Python.
Por defecto, AUTHENTICATION_BACKENDS está establecido en:
["django.contrib.auth.backends.ModelBackend"]
Ese es el backend de autenticación básico que comprueba la base de datos de usuarios de Django y consulta las permisos integrados. No proporciona protección contra ataques por fuerza bruta mediante ningún mecanismo de limitación de velocidad. Puedes implementar tu propio mecanismo de limitación de velocidad en un backend de autenticación personalizado o utilizar los mecanismos proporcionados por la mayoría de los servidores web.
La traducción de los textos es la siguiente:
Si un backend lanza una excepción PermissionDenied <class ~django.core.exceptions.PermissionDenied>, la autenticación fallará inmediatamente. Django no comprobará los backends que siguen.
Nota
Una vez que un usuario se ha autenticado, Django almacena qué backend se utilizó para autenticar al usuario en la sesión del usuario y reutiliza el mismo backend durante toda la sesión cada vez que se necesita acceso al usuario actualmente autenticado. Esto significa efectivamente que las fuentes de autenticación están cacheadas en una base por sesión, por lo que si cambias AUTHENTICATION_BACKENDS <setting.AUTHENTICATION_BACKENDS>, necesitarás eliminar los datos de sesión para obligar a los usuarios a reautenticarse con diferentes métodos. Una forma sencilla de hacerlo es ejecutar Session.objects.all().delete().
Un backend de autenticación es una clase que implementa dos métodos requeridos: get_user(user_id) y authenticate(request, **credentials), así como un conjunto de métodos relacionados con permisos opcionales <authorization_methods>.
El método get_user toma un user_id – que podría ser un nombre de usuario, ID de base de datos o lo que sea, pero tiene que ser la clave primaria de tu objeto de usuario – y devuelve un objeto de usuario o None.
El método authenticate toma un argumento request y credenciales como argumentos de palabra clave. La mayoría del tiempo, se verá así:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
Pero también podría autenticar un token, como se muestra a continuación:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
De cualquier manera, authenticate() debería comprobar las credenciales que recibe y devolver un objeto de usuario que coincida con esas credenciales si las credenciales son válidas. Si no lo son, debería devolver None.
request es una HttpRequest y puede ser None si no se proporcionó a la función authenticate <func ~django.contrib.auth.authenticate> (que lo pasa al backend).
La traducción de los textos es la siguiente:
La mejor forma de tratar con esto es crear un objeto de usuario de Django User para cada usuario que exista en su backend (por ejemplo, en su directorio LDAP, su base de datos SQL externa, etc.). Puede escribir un script para hacerlo con anticipación o su método authenticate puede hacerlo la primera vez que un usuario inicia sesión.
Aquí hay un ejemplo de backend que autentica contra una variable de nombre de usuario y contraseña definida en su archivo settings.py y crea un objeto de usuario de Django User la primera vez que un usuario se autentica. En este ejemplo, el objeto de usuario de Django creado es un superusuario que tendrá acceso completo al administrador:
from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class SettingsBackend(BaseBackend):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""
def authenticate(self, request, username=None, password=None):
login_valid = settings.ADMIN_LOGIN == username
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Create a new user. There's no need to set a password
# because only the password from settings.py is checked.
user = User(username=username) # is_active defaults to True.
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Para crear permisos personalizados para un objeto modelo determinado, utilice la propiedad Meta del modelo permissions.
Este ejemplo del modelo Task crea dos permisos personalizados, es decir, acciones que los usuarios pueden o no hacer con instancias de Task, específicas de su aplicación:
class Task(models.Model):
...
class Meta:
permissions = [
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
]
Lo único que hace esto es crear esos permisos adicionales cuando ejecutes manage.py migrate (la función que crea permisos está conectada a la señal post_migrate). Su código es responsable de verificar el valor de estos permisos cuando un usuario esté intentando acceder a la funcionalidad proporcionada por la aplicación (cambiando el estado de tareas o cerrando tareas). Continuando con el ejemplo anterior, el siguiente verifica si un usuario puede cerrar tareas:
user.has_perm("app.close_task")
User existente¶Hay dos formas de extender el modelo de usuario por defecto User sin sustituir su propio modelo. Si los cambios que necesitas son puramente comportamentales y no requieren ningún cambio en lo almacenado en la base de datos, puedes crear un modelo proxy basado en User. Esto permite cualquier de las características ofrecidas por modelos proxy, incluyendo el ordenamiento predeterminado, administradores personalizados o métodos del modelo personalizados.
Si deseas almacenar información relacionada con User, puedes utilizar un OneToOneField a un modelo que contenga los campos para la información adicional. Este modelo uno-a-uno se llama a menudo perfil, ya que podría almacenar información no relacionada con autenticación sobre un usuario del sitio. Por ejemplo, podrías crear un modelo Empleado:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
Asumiendo que existe un empleado Fred Smith quien tiene tanto un modelo de usuario como un modelo de empleado, puedes acceder a la información relacionada utilizando las convenciones estándar de modelos relacionados de Django:
>>> u = User.objects.get(username="fsmith")
>>> freds_department = u.employee.department
Para agregar campos del modelo perfil a la página de usuario en el administrador, define un InlineModelAdmin (para este ejemplo, usaremos un StackedInline) en tu archivo admin.py y agrégalo a una clase UserAdmin que está registrada con la clase User:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = "employee"
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = [EmployeeInline]
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Estos modelos de perfil no son especiales en ningún sentido - son simplemente modelos Django que suceden tener un enlace uno-a-uno con un modelo de usuario. Como tal, no se crean automáticamente cuando se crea un usuario, pero podría usarse el django.db.models.signals.post_save para crear o actualizar modelos relacionados según sea necesario.
El uso de modelos relacionados resulta en consultas adicionales o uniones para recuperar los datos relacionados. Dependiendo de tus necesidades, un modelo de usuario personalizado que incluya los campos relacionados puede ser tu mejor opción, sin embargo, las relaciones existentes con el modelo de usuario por defecto dentro de las aplicaciones de tu proyecto pueden justificar la carga adicional en la base de datos.
User personalizado¶Algunos tipos de proyectos pueden tener requisitos de autenticación para los cuales el modelo de usuario incorporado de Django no es siempre apropiado. Por ejemplo, en algunos sitios puede ser más sentido usar una dirección de correo electrónico como tu token de identificación en lugar de un nombre de usuario.
Django te permite sobreescribir el modelo de usuario por defecto proporcionando un valor para la configuración AUTH_USER_MODEL que hace referencia a un modelo personalizado:
AUTH_USER_MODEL = "myapp.MyUser"
Esta pareja de puntos describe el label del paquete Django (que debe estar en tu INSTALLED_APPS), y el nombre del modelo Django que deseas usar como modelo de usuario.
Si estás iniciando un nuevo proyecto, puedes configurar un modelo de usuario personalizado que se comporte de manera idéntica al modelo de usuario por defecto heredando de AbstractUser:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
No olvides apuntar a él la configuración AUTH_USER_MODEL. Hazlo antes de crear cualquier migración o ejecutar manage.py migrate por primera vez.
También, regístrate el modelo en el archivo admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
Cambiar el modelo de usuario (AUTH_USER_MODEL) después de haber creado las tablas de la base de datos es posible, pero puede ser complejo, ya que afecta a las claves foráneas y relaciones muchos-a-muchos, por ejemplo.
Esta modificación no se puede realizar automáticamente y requiere la corrección manual de tu esquema, el movimiento de tus datos desde la tabla de usuarios antigua, y posiblemente la reaplicación manual de algunas migraciones. Consulta #25313 para obtener un resumen de los pasos.
Debido a las limitaciones de la característica de dependencia dinámica de Django para modelos intercambiables, el modelo referenciado por AUTH_USER_MODEL debe crearse en la primera migración de su aplicación (generalmente llamada 0001_initial); de lo contrario, tendrás problemas de dependencia.
Además, es posible que te encuentres con un CircularDependencyError cuando ejecutes tus migraciones ya que Django no podrá romper automáticamente el bucle de dependencia debido a la dependencia dinámica. Si ves este error, debes romper el bucle moviendo los modelos sobre los cuales depende tu modelo de usuario en una segunda migración. (Puedes intentar creando dos modelos normales que tengan un ForeignKey entre sí y ver cómo makemigrations resuelve esa dependencia circular si quieres saber cómo se hace normalmente.)
AUTH_USER_MODEL¶Las aplicaciones reutilizables no deben implementar un modelo de usuario personalizado. Un proyecto puede utilizar muchas aplicaciones y dos aplicaciones reutilizables que implementaron un modelo de usuario personalizado no podrían usarse juntas. Si necesitas almacenar información por usuario en tu aplicación, utiliza ForeignKey o OneToOneField a settings.AUTH_USER_MODEL tal como se describe a continuación.
User¶Si haces referencia directamente a User (por ejemplo, haciendo referencia a él en una clave foránea), tu código no funcionará en proyectos donde el parámetro de configuración AUTH_USER_MODEL se haya modificado para utilizar un modelo de usuario diferente.
En lugar de referirse directamente a la clase User, debes hacer referencia al modelo de usuario utilizando django.contrib.auth.get_user_model(). Este método devuelve el modelo de usuario actualmente activo – el modelo de usuario personalizado si se especifica uno, o User en caso contrario.
When defines una clave foránea o relaciones muchos-a-muchos con el modelo de usuario, debes especificar el modelo personalizado utilizando la configuración AUTH_USER_MODEL. Por ejemplo:
from django.conf import settings
from django.db import models
class Article(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
Cuando se conecta a las señales enviadas por el modelo de usuario, debes especificar el modelo personalizado utilizando la configuración AUTH_USER_MODEL. Por ejemplo:
from django.conf import settings
from django.db.models.signals import post_save
def post_save_receiver(sender, instance, created, **kwargs):
pass
post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
En general, es más fácil referirse al modelo de usuario con la configuración AUTH_USER_MODEL en el código que se ejecuta en tiempo de importación, sin embargo, también es posible llamar a get_user_model() mientras Django está importando modelos, por lo que podrías usar models.ForeignKey(get_user_model(), ...).
Si tu aplicación se prueba con múltiples modelos de usuario, utilizando @override_settings(AUTH_USER_MODEL=...) por ejemplo, y caches el resultado de get_user_model() en una variable de nivel de módulo, es posible que necesites escuchar el setting_changed señal para limpiar la caché. Por ejemplo:
from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.signals import setting_changed
from django.dispatch import receiver
@receiver(setting_changed)
def user_model_swapped(*, setting, **kwargs):
if setting == "AUTH_USER_MODEL":
apps.clear_cache()
from myapp import some_module
some_module.UserModel = get_user_model()
Cuando comiences tu proyecto con un modelo de usuario personalizado, detén a considerar si esta es la elección correcta para tu proyecto.
Mantener toda la información relacionada con el usuario en un solo modelo elimina la necesidad de consultas adicionales o más complejas para recuperar modelos relacionados. Por otro lado, puede ser más adecuado almacenar información de usuario específica de la aplicación en un modelo que tenga una relación con tu modelo de usuario personalizado. Esto permite a cada aplicación especificar sus propias requisitos de datos del usuario sin potencialmente conflictuar o romper suposiciones de otras aplicaciones. También significa que mantendrás tu modelo de usuario lo más simple posible, enfocado en la autenticación y siguiendo los requisitos mínimos que Django espera de modelos de usuario personalizados.
Si utilizas el backend de autenticación predeterminado, entonces tu modelo debe tener un campo único que pueda usarse para fines de identificación. Esto puede ser un nombre de usuario, una dirección de correo electrónico o cualquier otro atributo único. Un campo de nombre de usuario no único es permitido si utilizas un backend de autenticación personalizado que lo pueda soportar.
La forma más fácil de construir un modelo de usuario personalizado compatible es heredar de AbstractBaseUser. AbstractBaseUser proporciona la implementación básica de un modelo de usuario, incluyendo contraseñas hash y reseñas de contraseña tokenizadas. Debes proporcionar luego algunos detalles de implementación clave:
Una cadena que describe el nombre del campo en el modelo de usuario que se utiliza como identificador único. Esto suele ser un nombre de usuario de alguna clase, pero también puede ser una dirección de correo electrónico o cualquier otro identificador único. El campo debe ser único (por ejemplo, tener unique=True configurado en su definición), a menos que utilices un backend de autenticación personalizado que pueda soportar nombres de usuario no únicos.
Los textos traducidos son:
class MyUser(AbstractBaseUser):
identifier = models.CharField(max_length=40, unique=True)
...
USERNAME_FIELD = "identifier"
Una cadena que describe el nombre del campo de correo electrónico en el modelo User. Este valor se devuelve por get_email_field_name().
Una lista de los nombres de campos que se solicitarán cuando se cree un usuario mediante la orden de comando de administración createsuperuser. El usuario deberá proporcionar un valor para cada uno de estos campos. Debe incluir cualquier campo para el cual blank es False o indefinido y puede incluir campos adicionales que desee solicitar cuando se crea un usuario interactivamente. REQUIRED_FIELDS no tiene efecto en otras partes de Django, como crear un usuario en la administración.
Por ejemplo, aquí está la definición parcial para un modelo de usuario que define dos campos requeridos - una fecha de nacimiento y una altura:
class MyUser(AbstractBaseUser):
...
date_of_birth = models.DateField()
height = models.FloatField()
...
REQUIRED_FIELDS = ["date_of_birth", "height"]
Nota
REQUIRED_FIELDS debe contener todos los campos requeridos en su modelo de usuario, pero no debe incluir el USERNAME_FIELD o password ya que estos campos se solicitarán siempre.
Una atributo booleano que indica si el usuario se considera «activo». Este atributo se proporciona como un atributo en AbstractBaseUser con valor por defecto de True. Cómo implementarlo dependerá de los detalles de sus backends autenticación elegidos. Consulte la documentación del is_active atributo en el modelo de usuario predeterminado para obtener más información.
Opcional. Un identificador formal más largo para el usuario, como su nombre completo. Si se implementa, aparece junto al nombre de usuario en la historia del objeto en django.contrib.admin.
Opcional. Un identificador informal corto para el usuario, como su primer nombre. Si se implementa, reemplaza el nombre de usuario en la saludo al usuario en la cabecera de django.contrib.admin.
Importando AbstractBaseUser
AbstractBaseUser y BaseUserManager son importables desde django.contrib.auth.base_user para que puedan ser importados sin incluir django.contrib.auth en INSTALLED_APPS.
Los siguientes atributos y métodos están disponibles en cualquier subclase de AbstractBaseUser:
Devuelve el valor del campo nominado por USERNAME_FIELD.
Normaliza el nombre de usuario llamando a normalize_username(). Si sobreescribes este método, asegúrate de llamar a super() para mantener la normalización.
Devuelve el nombre del campo de correo electrónico especificado por el atributo EMAIL_FIELD. Por defecto es 'email' si EMAIL_FIELD no está especificado.
Aplica la normalización Unicode NFKC a los nombres de usuario para que caracteres visiblemente idénticos con diferentes puntos de código Unicode se consideren idénticos.
Atributo de solo lectura que siempre es True (a diferencia de AnonymousUser.is_authenticated, que siempre es False). Esta es una forma de determinar si el usuario ha sido autenticado. Esto no implica ninguna autorización y no verifica si el usuario está activo o tiene una sesión válida. Aunque normalmente verificarás este atributo en request.user para encontrar si se ha poblado por la AuthenticationMiddleware (representando al usuario actualmente conectado), debes saber que este atributo es True para cualquier instancia de User.
Atributo de solo lectura que siempre es False. Esta es una forma de diferenciar objetos User y AnonymousUser. Generalmente, debes preferir usar el atributo is_authenticated en lugar de este.
Establece la contraseña del usuario a la cadena bruta dada, tomando cuidado del almacenamiento de contraseñas. No guarda el objeto AbstractBaseUser.
Cuando raw_password es None, la contraseña se establecerá en una contraseña inutilizable, como si se hubiera utilizado set_unusable_password().
Versión asíncrona: acheck_password()
Returns Verdadero si la cadena de texto bruta dada es la contraseña correcta para el usuario. (Esto se encarga del enmascaramiento de contraseñas al realizar la comparación.)
Marca al usuario como no tener contraseña establecida. Esto no es lo mismo que tener una cadena vacía como contraseña. check_password() para este usuario nunca devolverá Verdadero. No guarda el objeto AbstractBaseUser.
Puede necesitar esto si la autenticación de su aplicación tiene lugar contra una fuente externa existente, como un directorio LDAP.
Devuelve Falso si se ha llamado a set_unusable_password() para este usuario.
Devuelve un HMAC de el campo contraseña. Utilizado por Invalidez de sesión en cambio de contraseña.
Yields el HMAC del campo contraseña utilizando SECRET_KEY_FALLBACKS. Utilizado por get_user().
Las clases AbstractUser heredan de AbstractBaseUser:
Normaliza el correo electrónico llamando a BaseUserManager.normalize_email(). Si sobreescribes este método, asegúrate de llamar a super() para mantener la normalización.
También debes definir un administrador personalizado para tu modelo de usuario. Si tu modelo de usuario define los campos username, email, is_staff, is_active, is_superuser, last_login y date_joined igual que el modelo de usuario por defecto de Django, puedes instalar el administrador de usuarios de Django; sin embargo, si tu modelo de usuario define campos diferentes, necesitarás definir un administrador personalizado que extienda BaseUserManager proporcionando dos métodos adicionales:
El prototype de create_user() debería aceptar el campo de usuario, más todos los campos requeridos como argumentos. Por ejemplo, si tu modelo de usuario utiliza email como campo de usuario y tiene date_of_birth como campo requerido, entonces create_user debería estar definido como:
def create_user(self, email, date_of_birth, password=None):
# create user here
...
El prototype de create_superuser() debería aceptar el campo de usuario, más todos los campos requeridos como argumentos. Por ejemplo, si tu modelo de usuario utiliza email como campo de usuario y tiene date_of_birth como campo requerido, entonces create_superuser debería estar definido como:
def create_superuser(self, email, date_of_birth, password=None):
# create superuser here
...
Para un ForeignKey en el campo de usuario (USERNAME_FIELD) o los campos requeridos (REQUIRED_FIELDS), estos métodos reciben el valor del campo to_field (el primary_key por defecto) de una instancia existente.
La clase ~django.contrib.auth.models.BaseUserManager proporciona los siguientes métodos útiles:
Normaliza direcciones de correo electrónico bajando la parte del dominio de la dirección de correo electrónico.
Versión asíncrona: aget_by_natural_key()
Recupera una instancia de usuario utilizando el contenido del campo nominado por USERNAME_FIELD.
Se agregó el método aget_by_natural_key().
Si estás completamente satisfecho con el modelo de usuario de Django (User), pero quieres agregar información de perfil adicional, podrías heredar de django.contrib.auth.models.AbstractUser y agregar tus campos de perfil personalizados, aunque te recomendamos un modelo separado como se describe en Especificar un modelo de usuario personalizado. AbstractUser proporciona la implementación completa del modelo de usuario predeterminado (User) como un modelo abstracto.
Las formas y vistas integradas de Django (forms y views) hacen ciertas suposiciones sobre el modelo de usuario con el que están trabajando.
Las siguientes formas son compatibles con cualquier subclase de AbstractBaseUser:
AuthenticationForm: Utiliza el campo de nombre de usuario especificado por USERNAME_FIELD.
Las siguientes formas hacen suposiciones sobre el modelo de usuario y pueden usarse tal como están si se cumplen esas suposiciones:
PasswordResetForm: Supone que el modelo de usuario tiene un campo que almacena la dirección de correo electrónico del usuario con el nombre devuelto por get_email_field_name() (email por defecto) que se puede utilizar para identificar al usuario y un campo booleano llamado is_active para evitar los restablecimientos de contraseña para usuarios inactivos.
Finalmente, las siguientes formas están ligadas a User y deben ser reescritas o extendidas para funcionar con un modelo de usuario personalizado:
UsuarioCreacionFormulario
UsuarioModificacionFormulario
Si tu modelo de usuario personalizado es una subclase de AbstractUser, entonces puedes extender estos formularios de la siguiente manera:
from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields + ("custom_field",)
django.contrib.admin¶Si deseas que tu modelo de usuario personalizado también funcione con el administrador, tu modelo de usuario debe definir algunas atributos y métodos adicionales. Estos métodos permiten al administrador controlar el acceso del usuario a contenido administrativo:
Devuelve True si el usuario está autorizado para tener acceso al sitio administrativo.
Devuelve True si la cuenta de usuario actualmente está activa.
Devuelve True si el usuario tiene la permiso nombrado. Si se proporciona obj, el permiso debe comprobarse contra una instancia específica del objeto.
Devuelve True si el usuario tiene permiso para acceder a modelos en la aplicación dada.
También necesitarás registrar tu modelo de usuario personalizado con el administrador. Si tu modelo de usuario personalizado extiende django.contrib.auth.models.AbstractUser, puedes utilizar la clase existente django.contrib.auth.admin.UsuarioAdmin de Django. Sin embargo, si tu modelo de usuario extiende AbstractBaseUser, deberás definir una clase ModelAdmin personalizada. Es posible que puedas heredar la clase por defecto django.contrib.auth.admin.UsuarioAdmin; sin embargo, deberás sobrescribir cualquier de las definiciones que se refieren a campos en django.contrib.auth.models.AbstractUser que no están en tu clase de usuario personalizado.
Nota
Si estás utilizando un administrador de modelos personalizado que es una subclase de django.contrib.auth.admin.UserAdmin, entonces debes agregar tus campos personalizados a fieldsets (para campos utilizados en la edición de usuarios) y a add_fieldsets (para campos utilizados al crear un usuario). Por ejemplo:
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
...
fieldsets = UserAdmin.fieldsets + ((None, {"fields": ["custom_field"]}),)
add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ["custom_field"]}),)
Ver un ejemplo completo para obtener más detalles.
Para incluir fácilmente el marco de permisos de Django en tu propia clase de usuario, Django proporciona PermissionsMixin. Este es un modelo abstracto que puedes incluir en la jerarquía de clases para tu modelo de usuario, lo que te da todos los métodos y campos de base de datos necesarios para apoyar el modelo de permisos de Django.
PermissionsMixin proporciona los siguientes métodos y atributos:
Boolean. Indica que este usuario tiene todos los permisos sin asignarlos explícitamente.
Devuelve un conjunto de cadenas de permisos que tiene directamente el usuario.
Si se pasa obj, solo devuelve las permisos del usuario para este objeto específico.
Devuelve un conjunto de cadenas de permisos que tiene el usuario a través de sus grupos.
Si se pasa obj, solo devuelve las permisos de grupo para este objeto específico.
Returns a set of permission strings that the usuario has, both through group and user permissions.
Si obj se pasa como parámetro, solo devuelve las permisos para este objeto específico.
Devuelve True si el usuario tiene la permiso especificada, donde perm está en formato "<app label>.<permission codename>" (consulte permissions). Si User.is_active y is_superuser son ambos True, este método siempre devuelve True.
Si obj se pasa como parámetro, este método no comprobará la permiso para el modelo, sino para este objeto específico.
Devuelve True si el usuario tiene cada uno de los permisos especificados, donde cada perm es en formato "<app label>.<permission codename>". Si User.is_active y is_superuser son ambos True, este método siempre devuelve True.
Si obj se pasa como parámetro, este método no comprobará las permisos para el modelo, sino para el objeto específico.
Devuelve True si el usuario tiene alguna permiso en la paquete dada (la etiqueta de aplicación Django). Si User.is_active y is_superuser son ambos True, este método siempre devuelve True.
Una limitación de los modelos de usuarios personalizados es que instalar un modelo de usuario personalizado romperá cualquier modelo de proxy extendiendo User. Los modelos de proxy deben estar basados en una clase base concreta; al definir un modelo de usuario personalizado, se elimina la capacidad de Django para identificar de manera fiable la clase base.
Si tu proyecto utiliza modelos de proxy, debes modificar el proxy para que extienda el modelo de usuario en uso en tu proyecto, o fusionar el comportamiento del proxy con tu User subclass.
Aquí tienes un ejemplo de una aplicación de usuarios personalizados compatible con la administración. Este modelo de usuario utiliza una dirección de correo electrónico como nombre de usuario y tiene una fecha de nacimiento obligatoria; no proporciona ninguna comprobación de permisos más allá de una bandera admin en la cuenta del usuario. Este modelo sería compatible con todas las formas y vistas de autenticación integradas, excepto para las formas de creación de usuarios. Este ejemplo ilustra cómo funcionan la mayoría de los componentes juntos, pero no está destinado a copiarse directamente en proyectos para uso de producción.
Este código viviría en un archivo models.py para una aplicación de autenticación personalizada:
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError("Users must have an email address")
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, date_of_birth, password=None):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name="email address",
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["date_of_birth"]
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
Luego, para registrar este modelo de usuario personalizado con la administración de Django, serían necesarios los siguientes códigos en el archivo admin.py de la aplicación:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from customauth.models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(
label="Password confirmation", widget=forms.PasswordInput
)
class Meta:
model = MyUser
fields = ["email", "date_of_birth"]
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = MyUser
fields = ["email", "password", "date_of_birth", "is_active", "is_admin"]
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ["email", "date_of_birth", "is_admin"]
list_filter = ["is_admin"]
fieldsets = [
(None, {"fields": ["email", "password"]}),
("Personal info", {"fields": ["date_of_birth"]}),
("Permissions", {"fields": ["is_admin"]}),
]
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = [
(
None,
{
"classes": ["wide"],
"fields": ["email", "date_of_birth", "password1", "password2"],
},
),
]
search_fields = ["email"]
ordering = ["email"]
filter_horizontal = []
# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
Finalmente, especifica el modelo personalizado como modelo de usuario por defecto para tu proyecto utilizando la configuración AUTH_USER_MODEL en tu archivo settings.py:
AUTH_USER_MODEL = "customauth.MyUser"
Para optimizar el rendimiento cuando se llama desde un contexto asíncrono, los backends de autenticación pueden implementar versiones asíncronas de cada función - aget_user(user_id) y aauthenticate(request, **credentials). Cuando un backend de autenticación extiende BaseBackend y no se proporcionan versiones asíncronas de estas funciones, se sintetizarán automáticamente con sync_to_async. Esto tiene penalidades de rendimiento.
Si bien una interfaz asíncrona es opcional, siempre se requiere una interfaz síncrona. No hay síntesis automática para una interfaz síncrona si se implementa una interfaz asíncrona.
Django dispone de respaldos de autenticación nativos con soporte asincrónico por defecto. Si estos respaldos nativos se extienden, ten especial cuidado de asegurarte de que las versiones asincrónicas de las funciones modificadas también lo están.
may 31, 2026