User Model Personalizzato
Django raccomanda di definire un modello utente personalizzato allâinizio di ogni nuovo progetto, anche se il modello predefinito sembra sufficiente. Cambiare il modello utente dopo aver eseguito le migrazioni e estremamente complicato, quindi e meglio farlo subito.
Perche Personalizzare il Modello User
Il modello User predefinito di Django richiede username e password. In molti progetti moderni pero lâautenticazione avviene tramite email. Un modello personalizzato permette di:
- Usare lâemail come campo di login
- Aggiungere campi personalizzati direttamente al modello utente
- Avere il pieno controllo sul comportamento dellâautenticazione
Approccio 1: AbstractUser
AbstractUser estende il modello utente predefinito mantenendo tutti i campi e le funzionalita esistenti. E lâapproccio piu semplice:
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
telefono = models.CharField(max_length=20, blank=True)
data_nascita = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
bio = models.TextField(max_length=500, blank=True)
def __str__(self):
return self.email
Configura il modello nelle impostazioni:
# settings.py
AUTH_USER_MODEL = 'accounts.CustomUser'
Registra il modello nellâadmin:
# accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ['username', 'email', 'telefono', 'is_staff']
fieldsets = UserAdmin.fieldsets + (
('Informazioni Aggiuntive', {
'fields': ('telefono', 'data_nascita', 'avatar', 'bio'),
}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
('Informazioni Aggiuntive', {
'fields': ('email', 'telefono'),
}),
)
Approccio 2: AbstractBaseUser
AbstractBaseUser offre il massimo controllo ma richiede di definire tutto da zero. Usalo quando hai bisogno di un modello utente completamente diverso:
# accounts/models.py
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
nome = models.CharField(max_length=50)
cognome = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email' # campo usato per il login
REQUIRED_FIELDS = ['nome', 'cognome'] # campi richiesti da createsuperuser
objects = CustomUserManager()
def __str__(self):
return self.email
def get_full_name(self):
return f"{self.nome} {self.cognome}"
def get_short_name(self):
return self.nome
Custom User Manager
Con AbstractBaseUser devi definire un manager personalizzato:
# accounts/models.py
from django.contrib.auth.models import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, nome, cognome, password=None, **extra_fields):
if not email:
raise ValueError("L'indirizzo email e obbligatorio.")
email = self.normalize_email(email)
user = self.model(email=email, nome=nome, cognome=cognome, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, nome, cognome, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError("Il superuser deve avere is_staff=True.")
if extra_fields.get('is_superuser') is not True:
raise ValueError("Il superuser deve avere is_superuser=True.")
return self.create_user(email, nome, cognome, password, **extra_fields)
Usare get_user_model()
In tutto il progetto, per riferirsi al modello utente usa sempre get_user_model() invece di importare direttamente il modello:
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Articolo(models.Model):
autore = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='articoli'
)
titolo = models.CharField(max_length=200)
Nelle impostazioni e nei file di configurazione usa la stringa AUTH_USER_MODEL:
from django.conf import settings
from django.db import models
class Commento(models.Model):
autore = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
testo = models.TextField()
La sintassi settings.AUTH_USER_MODEL e preferita nei modelli perche funziona correttamente con il sistema di migrazioni.
Considerazioni sulle Migrazioni
E fondamentale definire AUTH_USER_MODEL prima di eseguire la prima migrazione:
# 1. Crea l'app accounts
# python manage.py startapp accounts
# 2. Definisci il modello CustomUser in accounts/models.py
# 3. Aggiungi AUTH_USER_MODEL in settings.py
# AUTH_USER_MODEL = 'accounts.CustomUser'
# 4. Aggiungi 'accounts' a INSTALLED_APPS
# 5. Crea e applica le migrazioni
# python manage.py makemigrations accounts
# python manage.py migrate
Se il progetto ha gia delle migrazioni con il modello User predefinito, cambiare AUTH_USER_MODEL richiede di ricreare il database da zero.
Form di Registrazione per User Personalizzato
Adatta il form di registrazione al modello personalizzato:
# accounts/forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import get_user_model
User = get_user_model()
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ['email', 'nome', 'cognome', 'password1', 'password2']
Conclusione
Personalizzare il modello utente e una delle prime decisioni da prendere in un progetto Django. AbstractUser e la scelta migliore nella maggior parte dei casi, poiche mantiene tutte le funzionalita predefinite aggiungendo i campi necessari. AbstractBaseUser offre il controllo totale ma richiede piu codice. In entrambi i casi, ricorda sempre di usare get_user_model() e settings.AUTH_USER_MODEL nel resto del progetto.