En tant que développeur expérimenté, vous êtes certainement à la recherche d’outils permettant de supprimer les tâches répétitives et d’accélérer le développement. En tant que novice, vous cherchez peut-être un moyen de respecter les standards REST sans trop vous poser de questions.

Dans les deux cas, Django Rest Framework (DRF) est une excellente solution. DRF est un framework d’API très utilisé, respectant les standards, et regorgeant de fonctionnalités, qui va non seulement vous faire gagner beaucoup de temps mais aussi vous montrer la bonne direction pour développer vos API RESTful. Plus particulièrement, DRF propose des vues génériques, c’est à dire des points d’accès à votre API déjà pré-construits.

Vous pourrez trouver le code ci-dessous dans un petit projet Django fonctionnel à cette adresse.

Concept

Les vues génériques de DRF sont parfaites pour les API simples qui font du CRUD (create, read, update, delete) basique sur la base de données sans trop de retraitement. Par exemple, disons que vous avez une table produit qui contient tous les produits de votre boutique et que vous souhaitez exposer ces produits tels quels à vos clients via une API. Alors c’est un cas idéal pour utiliser ListAPIView (cf. plus bas).

A partir de maintenant je vais supposer que vous avez installé Python, Django, DRF, et que vous maîtrisez les bases de Django.

Exemple de base 1 : lire les données

Créons un point d’accès API exposant tous les produits aux utilisateurs authentifiés seulement. Dans votre views.py faites ce qui suit :

from rest_framework import generics
from .serializers import ProductsSerializer

class GetProducts(generics.ListAPIView):
    """Return all products."""
    serializer_class = ProductsSerializer

ProductsSerializer est le serializer qui va convertir vos données issues de la base de données en données au format API. Ce serializer doit être écrit dans le fichier serializers.py et sera en charge de la récupération des données du model Product et de les transformer :

from rest_framework import serializers
from .models import Product

class ProductsSerializer(serializers.ModelSerializer):
    """Serialize products."""

    class Meta:
        model = Product
        fields = ("__all__")

Maintenant dans votre urls.py créez la route vers votre point d’accès :

from django.urls import path
from .views import GetProducts

urlpatterns = [
    path('get-products/', GetProducts.as_view(), name='get_products'),
]

Comme vous pouvez le voir, c’est simple comme bonjour puisque DRF se charge de presque tout tout seul. Vous avez désormais un point d’accès (/get-products/) que vous pouvez consommer via des requêtes HTTP get, et qui affiche vos produits au format API (habituellement json mais cela dépend, là aussi, de vos settings).

Exemple basique 2 : supprimer des données

Maintenant créons un point d’accès dédié à la suppression de produits par les utilisateurs authentifiés seulement. C’est encore plus simple puisque cela ne nécessite pas de sérialiser les données (en effet une fois le produit supprimé aucune donnée ne peut plus être retournée à l’utilisateur).

Dans views.py :

from rest_framework import generics

class DeleteProduct(generics.DestroyAPIView):
    """Remove product"""
    permission_classes = (permissions.IsAuthenticated,) # Limit to authenticated users only

Dans urls.py

from django.urls import path
from .views import DeleteProduct

urlpatterns = [
    path('delete-product/', DeleteProduct.as_view(), name='delete_product'),
]

Vous avez maintenant un point d’accès /delete-product/ qui permet de supprimer les produits un par un en utilisant les requêtes HTTP delete, et qui accepte seulement les requêtes authentifiées (le mécanisme d’authentification dépend de vos settings).

Personnaliser le comportement des vues génériques

Chaque vue générique peut être affinée via l’écriture d’une méthode get_queryset(). Par exemple disons que vous souhaitez uniquement afficher les produits ayant un flag active à True en base de données. Vous pouvez faire comme suit :

from rest_framework import generics
from .serializers import ProductsSerializer
from .model import Product

class GetActiveProducts(generics.ListAPIView):
    """Return all active products."""
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = ProductsSerializer

    def get_queryset(self):
        """Filter active products."""
        return Product.objects.filter(active=True)

get_queryset() est une méthode commune à toutes les vues génériques. Certaines vues génériques ont aussi leurs propres méthodes afin de contrôler plus précisément le comportement du point d’accès. Par exemple, disons que vous ne souhaitez pas vraiment supprimer les produits mais juste les marquer comme inactifs. Vous pourriez utiliser la méthode destroy() :

from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework import status

class DisableProduct(generics.DestroyAPIView):
    """Remove product"""
    permission_classes = (permissions.IsAuthenticated,)

    def destroy(self, request, pk):
        """
        By default, DestroyAPIView deletes the product from db.
        Here we only want to flag it as inactive.
        """
        product = get_object_or_404(self.get_queryset(), pk=pk)
        product.active = False
        product.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

Dans l’exemple ci-dessus nous essayons en premier de rechercher le produit que l’utilisateur veut supprimer. Si nous ne parvenons pas à le trouver, nous retournons un code 404 à l’utilisateur. Si le produit est marqué comme inactif avec succès, nous retournons un code 204 à l’utilisateur signifiant que le produit a été supprimé avec succès.

Les vues génériques sont parfaites pour les cas simples, et il est parfois plus sage d’utiliser les classiques APIView pour les cas spéciaux. Par exemple, disons que vous souhaitez non seulement retourner les produits à l’utilisateur mais aussi les enrichir avec des données ne se trouvant pas dans le model Product (ex : les commandes liées à ce produit, le fournisseur du produit, etc.). Dans ce cas, si vous vouliez utiliser les vues génériques, il vous faudrait définir de nouveaux champs dans le serializer grâce à des méthodes get_new_field() additionnelles qui peuvent facilement rendre votre serializer inutilement complexe…

Conclusion

Comme vous l’avez vu, les vues génériques de DRF rendent le développement d’API extrêmement simple grâce à un peu de magie. Toutefois gardez à l’esprit que les vues génériques ne peuvent pas s’appliquer à tous les cas d’usage et que parfois essayer à tout prix d’adapter les vue génériques à vos besoins sera plus difficile que de re-développer les choses de zéro par vous-même !

J’espère que vous avez aimé ce petit tuto. J’adorerais avoir vos retours !

Also available in English

Rate Limiting d'API avec Traefik, Docker, Go, et la mise en cache

Limiter l'utilisation de l'API en fonction d'une règle avancée de limitation du débit n'est pas si facile. Pour y parvenir derrière l'API NLP Cloud, nous utilisons une combinaison de Docker, Traefik (comme proxy inverse) et la mise en cache locale dans un script Go. Si cela est fait correctement, vous pouvez améliorer considérablement les performances de votre limitation de débit et étrangler correctement les demandes d'API sans sacrifier la vitesse des demandes. Continuer de lire