Come estendere l'User model di Django
Ho sviluppato diversi progetti in Django, ormai lo utilizzo da diversi anni e lo trovo fantastico sotto molti punti di vista. Tuttavia all’inizio ho avuto molte perplessità sulla pratica da adottare per “associare” delle informazioni personalizzate all’utente standard che il framework mette a disposizione. Aggiungere l’immagine del profilo, oppure l’indirizzo, o altri campi a seconda delle necessità del progetto.
Perchè ho avuto delle perplessità? Bhè innanzitutto perchè pasticciare con un componente così importante come l’autenticazione potrebbe compromettere tutto, portare a problemi di manutenzione o peggio di sicurezza.
Altro motivo è che non esiste un motodo unico per raggiungere lo scopo, le mie ricerche su Google non mi chiarivano per bene tutti i pro e contro delle varie metodologie, quindi ho dovuto testare con mano per capire a fondo.
Inizialmente poi non ero così esperto con il framework. Django è un framework vastissimo! Ha tantissime utility e ancora oggi spesso scopro funzioni che non conoscevo affatto o che vengono introdotte nelle ultime release.
Come ti dicevo, esistono più modi per raggiungere lo stesso scopo. Ti elencherò solo quelle che conosco e che ho provato personalmente, infine ti farò vedere il codice della soluzione che ritengo migliore e che continuo ad utilizzare attualmente nei miei progetti.
Creare un nuovo model che abbia una relazione uno ad uno con l’User di Django
Questa soluzione mi è piaciuta fin da subito ed è quella che ho usato per prima.
PRO:
- non è invasiva, Django non si accorge di nulla, lui contina a funzionare così come ha sempre fatto (bene);
- è semplice, se il tuo progetto è già avviato ed è in produzione da un po' (con l’utente standard) non devi risolvere conflitti o capire come creare una nuova migration, fai la tua relazione ed integri le informazioni che ti servono.
CONTRO:
- è pesante, se devi controllare continuamente i dati che hai appena collegato all’utente allora diventa un problema, ad ogni richiesta devi fare un’altra query per “pescare” i dati che ti servono;
- devi creare un signal
post_save
da agganciare all’utente Django per far si che ogni volta che crei un nuovo utente venga creato anche il tuo model collegato (altrimenti dovresti gestirti un po' di casi limite o condizioni qui e lì che sporcherebbero il codice).
A mio parere i contro sono pesanti, diciamo che adotterei questa strada solo se non avessi altra scelta.
Creare un nuovo model che estende AbstractUser
Qualche tempo dopo ho testato quest’altra soluzione, per me è stato subito amore! La trovo molto più pratica ed utile anche se più invasiva.
Attenzione: è AbstractUser
non AbstractBaseUser
PRO:
- sostituisce completamente l’utente Django, ad ogni richiesta tutte le tue informazioni aggiuntive sono lì, pronte per essere consultate, non devi fare altre query o giri strani;
CONTRO:
- altera la struttura della tabella dell’utente standard di Django, se il tuo progetto è già avviato e vuoi provare questa soluzione “a caldo”, probabilmente dovrai farti una migration ad-hoc (mi raccomando, fai sempre prima un bel backup).
Ogni volta che avvio un nuovo progetto questa è la prima operazione che faccio SEMPRE, anche se magari nell’immediato non necessito di campi extra, potrei averne bisogno in futuro.
Creare un nuovo model che estende AbstractBaseUser
Questa soluzione è molto simile alla precedente. Estendendo AbstractUser
avremmo a disposizione
tutti i campi del model User originale di Django con la possibilità di aggiungere solo i campi extra che ci interessano.
Con AbstractBaseUser
avremmo a disposizione solo la
funzionalità di autenticazione ma nessun campo aggiuntivo (username, nome, cognome, etc.).
Dovremmo provvedere noi a inserire tutti i campi che ci servono, quindi è una soluzione più a basso livello.
Non l’ho mai provata ma dovrebbe servire solo a chi ha esigenze davvero specifiche.
E adesso ti faccio vedere un po' di codice 😆
Crea una nuova app:
python manage.py startapp userapp
e definisci il model per il tuo utente:
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
profile_image = models.ImageField(upload_to="users/profile/", verbose_name="Immagine del profilo",
blank=True, null=True)
address = models.TextField(verbose_name="Indirizzo")
phone = models.CharField(max_length=32, verbose_name="Telefono")
# .. TODO aggiungi altri campi ..
class Meta:
verbose_name = 'MyUser'
verbose_name_plural = 'MyUsers'
Nel file settings.py
dovrai elencare l’app che hai appena creato nelle INSTALLED_APPS
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'userapp'
]
e aggiungere anche questa variabile:
AUTH_USER_MODEL = 'userapp.MyUser'
Ricordati di sostituire userapp
con il nome che vuoi creare nel tuo progetto.
Non ti resta che effettuare la migrazione:
python manage.py makemigrations
python manage.py migrate
Per visualizzare nell’admin tutte le informazioni del tuo nuovo user ed effettuare tutte le operazioni che puoi già
fare con l’utente Django devi modificare l' admin.py
di userapp
in questo modo:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
@admin.register(MyUser)
class CustomerAdmin(UserAdmin):
list_display = ['username', 'first_name', 'last_name']
fieldsets = UserAdmin.fieldsets + (('Extra fields', {'fields': ('profile_image', 'address', 'phone')}),)
Se tutto è andato a buon fine, nella tue views quando accedi alla variabile request.user
o self.request.user
Django ti restituirà un’instanza della classe che hai appena creato.
Semplice no? 😁
Scrivimi nei commenti se sei riuscito e cosa ne pensi.
Ti aspetto anche nel mio canale Telegram pubblico periodicamente sconti, news tech ed altro 🙂
AP