Pour construire des API avec Django REST Framwork (DRF), le composant de sérialisation est essentiel. Il gère la conversion des données Python complexes, comme les objets QuerySet et les instances de modèles, en types natifs (dict, list) compréhensibles par le client (sérialisation), et inversement (désérialisation) avec validation intégrée.
Modèle de base
Exemple avec un modèle simple :
class Article(models.Model):
titre = models.CharField(max_length=100)
prix = models.DecimalField(max_digits=10, decimal_places=2)
editeur = models.ForeignKey('Editeur', on_delete=models.CASCADE)
auteurs = models.ManyToManyField('Auteur')
Après l'application des migrations, insérez des données de test.
Utilisation de base avec Serializer
Créez un fichier serializers.py pour définir votre sérialiseur :
from rest_framework import serializers
class ArticleSerializer(serializers.Serializer):
titre = serializers.CharField(max_length=100)
prix = serializers.DecimalField(max_digits=10, decimal_places=2)
nom_editeur = serializers.CharField(source='editeur.nom')
Sérialisation pour la lecture des données
Dans la vue, initialisez le sérialiseur avec l'instance et le paramètre many approprié :
from rest_framework.views import APIView
from rest_framework.response import Response
class ListeArticlesView(APIView):
def get(self, requete):
articles = Article.objects.select_related('editeur').all()
serialiseur = ArticleSerializer(instance=articles, many=True)
return Response(serialiseur.data)
class DetailArticleView(APIView):
def get(self, requete, identifiant):
article = Article.objects.filter(pk=identifiant).first()
serialiseur = ArticleSerializer(instance=article)
return Response(serialiseur.data)
Désérialisation pour la création et la modification
Ajoutez les méthodes create et update au sérialiseur pour gérer l'écriture :
class ArticleSerializer(serializers.Serializer):
# ... champs ...
def create(self, donnees_validees):
return Article.objects.create(**donnees_validees)
def update(self, instance, donnees_validees):
instance.titre = donnees_validees.get('titre', instance.titre)
instance.prix = donnees_validees.get('prix', instance.prix)
instance.save()
return instance
Dans la vue, utilisez is_valid() et save() :
class ListeArticlesView(APIView):
def post(self, requete):
serialiseur = ArticleSerializer(data=requete.data)
if serialiseur.is_valid(raise_exception=True):
serialiseur.save()
return Response({'statut': 'succès'}, status=201)
return Response(serialiseur.errors, status=400)
class DetailArticleView(APIView):
def put(self, requete, identifiant):
article = Article.objects.get(pk=identifiant)
serialiseur = ArticleSerializer(article, data=requete.data)
if serialiseur.is_valid(raise_exception=True):
serialiseur.save()
return Response({'statut': 'mis à jour'})
return Response(serialiseur.errors, status=400)
Validation et hooks
DRF offre plusieurs niveaux de validation :
- Validation au niveau du champ (ex:
max_length). - Validateurs via le paramètre
validators. - Hook de validation locale (
validate_<nom_champ>). - Hook de validation globale (
validate).
class ArticleSerializer(serializers.Serializer):
# ...
def validate_titre(self, valeur):
if 'spam' in valeur.lower():
raise serializers.ValidationError("Le titre ne peut contenir 'spam'.")
return valeur
def validate(self, donnees):
if donnees.get('prix') < 0:
raise serializers.ValidationError("Le prix doit être positif.")
return donnees
Champs et paramètres courants
Champs de base : CharField, IntegerField, DecimalField, DateTimeField, BooleanField, ListField, DictField.
Paramètres importants : required, default, allow_null, read_only, write_only, source.
Personnalisation avancée
Avec SerializerMethodField
class ArticleSerializer(serializers.Serializer):
# ... champs ...
detail_editeur = serializers.SerializerMethodField()
liste_auteurs = serializers.SerializerMethodField()
def get_detail_editeur(self, obj):
return {'nom': obj.editeur.nom, 'site': obj.editeur.site}
def get_liste_auteurs(self, obj):
return [{'nom': auteur.nom} for auteur in obj.auteurs.all()]
Modèle avec propriétés
class Article(models.Model):
# ... champs ...
@property
def resume_prix(self):
return f"{self.prix} €"
# Dans le sérialiseur
class ArticleSerializer(serializers.ModelSerializer):
resume_prix = serializers.CharField(source='resume_prix', read_only=True)
class Meta:
model = Article
fields = ['titre', 'prix', 'resume_prix']
ModelSerializer
Il simplifie la défniition en générant automatiquement les champs et en implémentant create/update.
class ArticleSerializer(serializers.ModelSerializer):
detail_editeur = serializers.SerializerMethodField()
class Meta:
model = Article
fields = ['id', 'titre', 'prix', 'editeur', 'auteurs', 'detail_editeur']
extra_kwargs = {
'titre': {'max_length': 100},
'editeur': {'write_only': True},
'auteurs': {'write_only': True},
}
def get_detail_editeur(self, obj):
return {'nom': obj.editeur.nom}
Utilisez fields = '__all__' pour tous les champs, ou une liste explicite. extra_kwargs permet de surcharger les arguments des champs.
Requêtes avec relations
Pour la création/mise à jour avec des relations, le client envoie les identifiants :
# POST data: { "titre": "...", "prix": "...", "editeur": 1, "auteurs": [1, 2] }
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['titre', 'prix', 'editeur', 'auteurs']
def create(self, validated_data):
auteurs_data = validated_data.pop('auteurs')
article = Article.objects.create(**validated_data)
article.auteurs.set(auteurs_data)
return article