Serializer
I Serializer in Django REST Framework (DRF) sono responsabili della conversione dei dati complessi, come istanze di modelli Django e queryset, in tipi di dati Python nativi che possono essere facilmente renderizzati in JSON o altri formati. Gestiscono anche la deserializzazione, ovvero la validazione e la conversione dei dati in ingresso.
La Classe Serializer
La classe base Serializer permette di definire manualmente i campi e la logica di serializzazione.
from rest_framework import serializers
class LibroSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
titolo = serializers.CharField(max_length=200)
autore = serializers.CharField(max_length=100)
prezzo = serializers.DecimalField(max_digits=6, decimal_places=2)
pubblicato = serializers.BooleanField(default=False)
def create(self, validated_data):
return Libro.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.titolo = validated_data.get('titolo', instance.titolo)
instance.autore = validated_data.get('autore', instance.autore)
instance.prezzo = validated_data.get('prezzo', instance.prezzo)
instance.pubblicato = validated_data.get('pubblicato', instance.pubblicato)
instance.save()
return instance
ModelSerializer
ModelSerializer genera automaticamente i campi del serializer a partire da un modello Django, riducendo significativamente il codice da scrivere.
from rest_framework import serializers
from .models import Libro
class LibroSerializer(serializers.ModelSerializer):
class Meta:
model = Libro
fields = ['id', 'titolo', 'autore', 'prezzo', 'pubblicato']
# oppure tutti i campi:
# fields = '__all__'
# oppure escludi specifici campi:
# exclude = ['data_creazione']
ModelSerializer genera automaticamente i metodi create() e update(), i validatori basati sui vincoli del modello e i campi corrispondenti ai campi del modello.
Tipi di Campo Comuni
DRF offre numerosi tipi di campo per la serializzazione:
class ProdottoSerializer(serializers.ModelSerializer):
# Campi personalizzati aggiuntivi
nome = serializers.CharField(max_length=200)
descrizione = serializers.CharField(allow_blank=True)
prezzo = serializers.DecimalField(max_digits=10, decimal_places=2)
disponibile = serializers.BooleanField(default=True)
quantita = serializers.IntegerField(min_value=0)
data_aggiunta = serializers.DateTimeField(read_only=True)
immagine_url = serializers.URLField(required=False)
categoria = serializers.ChoiceField(choices=['elettronica', 'libri', 'abbigliamento'])
class Meta:
model = Prodotto
fields = '__all__'
Campi read_only e write_only
I campi possono essere configurati come sola lettura o sola scrittura per controllare il flusso dei dati.
class UtenteSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, min_length=8)
data_registrazione = serializers.DateTimeField(read_only=True)
class Meta:
model = Utente
fields = ['id', 'username', 'email', 'password', 'data_registrazione']
read_only_fields = ['id', 'data_registrazione']
def create(self, validated_data):
utente = Utente.objects.create_user(**validated_data)
return utente
I campi read_only vengono inclusi nella risposta ma ignorati durante la deserializzazione. I campi write_only vengono accettati in input ma non inclusi nella risposta (ideali per le password).
Validazione
DRF offre diversi livelli di validazione: a livello di campo, a livello di oggetto e tramite validatori personalizzati.
class OrdineSerializer(serializers.ModelSerializer):
class Meta:
model = Ordine
fields = ['id', 'prodotto', 'quantita', 'indirizzo']
# Validazione a livello di singolo campo
def validate_quantita(self, value):
if value <= 0:
raise serializers.ValidationError("La quantita deve essere positiva.")
if value > 100:
raise serializers.ValidationError("Non puoi ordinare piu di 100 pezzi.")
return value
# Validazione a livello di oggetto (campi multipli)
def validate(self, data):
if data.get('quantita', 0) > 10 and not data.get('indirizzo'):
raise serializers.ValidationError(
"Per ordini superiori a 10 pezzi, l'indirizzo e' obbligatorio."
)
return data
SerializerMethodField
SerializerMethodField permette di aggiungere campi calcolati al serializer, il cui valore viene determinato da un metodo personalizzato.
class LibroSerializer(serializers.ModelSerializer):
prezzo_scontato = serializers.SerializerMethodField()
nome_completo_autore = serializers.SerializerMethodField()
class Meta:
model = Libro
fields = ['id', 'titolo', 'prezzo', 'prezzo_scontato', 'nome_completo_autore']
def get_prezzo_scontato(self, obj):
return round(obj.prezzo * 0.9, 2)
def get_nome_completo_autore(self, obj):
return f"{obj.autore.nome} {obj.autore.cognome}"
Il metodo associato deve seguire la convenzione get_<nome_campo> e riceve lâistanza dellâoggetto come argomento.
Serializer Annidati
I serializer possono essere annidati per rappresentare relazioni tra modelli.
class AutoreSerializer(serializers.ModelSerializer):
class Meta:
model = Autore
fields = ['id', 'nome', 'cognome']
class LibroSerializer(serializers.ModelSerializer):
autore = AutoreSerializer(read_only=True)
autore_id = serializers.PrimaryKeyRelatedField(
queryset=Autore.objects.all(),
source='autore',
write_only=True
)
class Meta:
model = Libro
fields = ['id', 'titolo', 'autore', 'autore_id', 'prezzo']
Con questa struttura, la risposta GET include i dettagli completi dellâautore, mentre per creare un libro basta inviare lâautore_id.
I Metodi create() e update()
Personalizzare create() e update() permette di gestire logiche complesse durante il salvataggio.
class ProfiloSerializer(serializers.ModelSerializer):
competenze = serializers.ListField(child=serializers.CharField())
class Meta:
model = Profilo
fields = ['id', 'bio', 'competenze']
def create(self, validated_data):
competenze = validated_data.pop('competenze', [])
profilo = Profilo.objects.create(**validated_data)
for comp in competenze:
Competenza.objects.create(profilo=profilo, nome=comp)
return profilo
def update(self, instance, validated_data):
competenze = validated_data.pop('competenze', None)
instance.bio = validated_data.get('bio', instance.bio)
instance.save()
if competenze is not None:
instance.competenze.all().delete()
for comp in competenze:
Competenza.objects.create(profilo=instance, nome=comp)
return instance
Conclusione
I Serializer sono il cuore di Django REST Framework e svolgono un ruolo fondamentale nella trasformazione e validazione dei dati. Dalla classe base Serializer al piuâ pratico ModelSerializer, passando per la validazione a piuâ livelli, i campi calcolati con SerializerMethodField e i serializer annidati, padroneggiare questi strumenti eâ essenziale per costruire API robuste e ben strutturate.