Implémentation de sérialiseurs dans Django REST Framework

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

Étiquettes: django-rest-framework sérialisation Python api-rest modèles-django

Publié le 28 juin à 18h51