Les options comme les vues fonctionnelles avec décorateurs, les classes héritant d’APIView, ou l’utilisation des mixins, bien que plus explicites, nécessitent souvent davantage de code et d’efforts de maintenance. À l’opposé, les ViewSets offrent un haut niveau d’abstraction qui peut masquer la logique sous-jacente, rendant le code moins transparent pour certains développeurs. Le choix de GenericAPIView combiné à ModelSerializer permet de réduire la quantité de code tout en conservant une lisibilité suffisante.
1. Structure d’une interface RESTful avec Django REST Framework
Django repose sur l’architecture MVT (Modèle-Vue-Template). Dans ce schéma, le Modèle interagit avec la base de données, la Vue gère la logique métier, et le Template reçoit les requêtes et affiche les réponses (côté client). En mode non séparé, tout fonctionne naturellement. En mode front-end/back-end séparé, le Template disparaît et c’est le sérialiseur qui prend en charge la réception des requêtes et le renvoi des réponses. Ainsi, une interface API RESTful se compose de trois éléments principaux : le modèle, la vue et le sérialiseur.
1.1 Le modèle (models.py)
Dans l’architecture MVT, le modèle est responsable des interactions avec la base de données. Grâce à l’ORM, il suffit de mapper des objets Python à des tables via des classes et leurs champs. Avant cela, il est essentiel de concevoir un diagramme entité-relation (ERD) et de s’assurer que chaque entité respecte au moins la troisième forme normale (3NF).
# Les modèles restent les mêmes, que l’architecture soit séparée ou non.
1.2 Le sérialiseur (serializers.py)
En mode non séparé, le template s’occupe des requêtes et réponses. En mode séparé, c’est le sérialiseur qui remplit ce rôle : recevoir les données front-end et retourner les réponses. Avec ModelSerializer, la définition peut ressembler à ceci :
"""
Sérialiseur utilisant ModelSerializer
"""
from rest_framework import serializers
from .models import *
import re
class MonModeleSerializer(serializers.ModelSerializer):
class Meta:
model = MonModele
fields = ('nom', 'telephone')
extra_kwargs = {
'telephone': {
'write_only': True,
'min_length': 8,
'max_length': 16,
}
}
def validate_nom(self, nom):
if Utilisateur.objects.filter(username=nom).exists():
raise serializers.ValidationError("Ce nom d'utilisateur est déjà pris")
return nom
def validate_telephone(self, telephone):
if Utilisateur.objects.filter(mobile=telephone).exists():
raise serializers.ValidationError("Ce numéro est déjà enregistré")
REGEX_TEL = r'1[358]\d{9}$|^147\d{8}$|^176\d{8}$'
if not re.match(REGEX_TEL, telephone):
raise serializers.ValidationError("Format de téléphone invalide")
return telephone
def create(self, donnees_validees):
"""donnees_validees est un dictionnaire"""
instance = super().create(donnees_validees)
# Personnalisation possible ici
return instance
La notion de validation des champs peut prêter à confusion. Mais en rappelant la fonction du sérialiseur — recevoir les requêtes et retourner des réponses — on comprend mieux les mécanismes de sérialisation et désérialisation :
- Sérialisation : conversion d’un objet Python (issu de l’ORM) en format JSON (ou XML) transmissible.
- Désérialisation : conversion d’une chaîne JSON (provenant du front-end) en objet Python, afin d’interagir avec l’ORM.
Lors de la désérialisation, il est crucial de valider les données entrantes. Cela se fait via des contraintes dans extra_kwargs ou des méthodes de validation personnalisées. En cas de succès, une instance du modèle est renvoyée à la vue ; sinon, une erreur est retournée.
1.3 La vue (views.py)
Bien que le modèle et le sérialiseur soient essentiels, c’est la vue qui contient la logique métier principale. Avec GenericAPIView, une vue de création peut se présenter ainsi :
class MonModeleCreateView(generics.CreateAPIView):
# permission_classes = [permissions.IsAuthenticated]
queryset = MonModele.objects.all()
serializer_class = MonModeleSerializer
def post(self, requete, *args, **kwargs):
donnees_serialisees = MonModeleSerializer(data=requete.data)
if donnees_serialisees.is_valid():
return super().post(requete, *args, **kwargs)
else:
return Response(donnees_serialisees.errors)
def create(self, requete, *args, **kwargs):
champ = requete.POST.get("xx")
# Logique métier personnalisée
return JsonResponse(
data={'data': donnees, 'msg': 'OK', 'code': 1},
safe=False
)
En redéfinissant les méthodes get ou create, on accède aux données front-end via requete et on implémente les traitements spécifiques.
2. Étapes de développement d’une API DRF
- Conception du modèle : respect des formes normales et des relations.
- Définition du sérialiseur : identification des champs en entrée avec leurs contraintes.
- Implémentation de la vue : logique métier, permissions, filtres, pagination, etc.
Bien que l’ordre puisse varier selon les développeurs, cette séquence offre une approche structurée pour construire des interfaces API robustes.