Come estendere l'User model di Django

16-06-2021 python

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

 

Autore

Antonio Porcelli

Antonio Porcelli

@progressify


Se non visualizzi il blocco dei commenti è perchè non hai accettato i cookies.
Cancella le preferenze del tuo browser per questo sito, aggiorna la pagina ed accetta i cookies.