• 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 28/11/2023

Ajoutez de l’interaction avec les actions

Ne soyez pas trop actif !

Une action, on a déjà parlé de ça, non ?

Nous avons parlé du paramètre action  des Viewsets, mais dans ce chapitre nous allons voir le décorateur action,  fourni par DRF, qui permet de réaliser d’autres types d’actions que les classiques du CRUD, comme « Demander en ami », « S’abonner à un fil d’actualité » ou « Publier un article ».

Vous devez penser Action chaque fois qu’un besoin fait référence à une entité, mais que le verbe ne correspond pas à un élément du CRUD. Par exemple, dans « Nous souhaitons que nos visiteurs puissent liker des publications », l’entité est la publication et l’action est liker.

Une action se crée dans DRF en mettant en place le décorateur action  sur une méthode d’un Viewset. Les paramètres suivants sont disponibles :

  • methods  est la liste des méthodes HTTP qui appellent cette action, parmi GET, POST, PATCH, PUT, DELETE.

  • detail  est un booléen qui précise si l’action est disponible sur l’URL de liste ou de détail.

  • url_path  permet de déterminer l’URL qui sera ajoutée à la fin de l'endpoint de liste ou de détail. S'il n’est pas précisé, alors le nom de la méthode est utilisé.

Pour notre boutique en ligne, on pourrait imaginer une action qui permette d’activer ou de désactiver une catégorie.

Mais on ne pourrait pas juste faire un PATCH sur la catégorie pour mettre active  à False  ?

On pourrait effectivement, mais nous aimerions également désactiver tous les produits qui composent cette catégorie. Plutôt que de laisser les applications clientes faire tous ces appels, mettons-leur à disposition un seul endpoint qui réalise cela.

D’ailleurs, vous savez quoi ? On va le faire tout de suite… ;)

Soyez actif quand même !

Les actions se mettent en place sur les Viewsets, alors allons modifier notre CategoryViewset  pour lui ajouter une action disable  que nous souhaitons accessible en POST.

 @transaction.atomic
    @action(detail=True, methods=['post'])
    def disable(self, request, pk):
        # Nous avons défini notre action accessible sur la méthode POST seulement
        # elle concerne le détail car permet de désactiver une catégorie

        # Nous avons également mis en place une transaction atomique car plusieurs requêtes vont être exécutées
        # en cas d'erreur, nous retrouverions alors l'état précédent

        # Désactivons la catégorie
        category = self.get_object()
        category.active = False
        category.save()

        # Puis désactivons les produits de cette catégorie
        category.products.update(active=False)

        # Retournons enfin une réponse (status_code=200 par défaut) pour indiquer le succès de l'action
        return Response()

Ainsi, la réalisation d’un POST sur l'endpointhttp://127.0.0.1:8000/api/category/2/disable/  aura pour effet de :

Ajoutons une méthode disable  à notre model Category  :

class Category(models.Model):
 
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True)
 
    name = models.CharField(max_length=255)
    active = models.BooleanField(default=False)
 
    @transaction.atomic
    def disable(self):
        if self.active is False:
        # Ne faisons rien si la catégorie est déjà désactivée
            return
        self.active = False
        self.save()
        self.products.update(active=False)
class MultipleSerializerMixin:
    # Un mixin est une classe qui ne fonctionne pas de façon autonome
    # Elle permet d'ajouter des fonctionnalités aux classes qui les étendent

    detail_serializer_class = None

    def get_serializer_class(self):
        # Notre mixin détermine quel serializer à utiliser
        # même si elle ne sait pas ce que c'est ni comment l'utiliser
        if self.action == 'retrieve' and self.detail_serializer_class is not None:
            # Si l'action demandée est le détail alors nous retournons le serializer de détail
            return self.detail_serializer_class
        return super().get_serializer_class()

class CategoryViewset(MultipleSerializerMixin, ReadOnlyModelViewSet):
 
    serializer_class = CategoryListSerializer
    detail_serializer_class = CategoryDetailSerializer
 
    @action(detail=True, methods=['post'])
    def disable(self, request, pk):
        # Nous pouvons maintenant simplement appeler la méthode disable
        self.get_object().disable()
        return Response()

Cette modification ne change en rien le fonctionnement de notre API, mais permet une lecture plus claire du code. L’action ne fait qu'appeler une méthode de notre model Category.

Suivez-moi dans le screencast ci-dessous sur la mise en place de notre action  disable  : 

À vous de jouer

Mettons en place le même système pour désactiver un produit qui désactive également les articles associés. Nous pourrions également améliorer la désactivation d’une catégorie en désactivant les articles de chaque produit.

Pour réaliser cela, vous pouvez partir de la branche P2C3_exercice. Elle contient déjà ce que nous venons de faire ensemble. Une solution est proposée sur la branche P2C3_solution.

En résumé

  • Il est possible de créer d’autres actions en dehors de celles du CRUD.

  • DRF met à disposition un décorateur action  qui permet de créer de nouvelles actions.

  • Les actions peuvent être mises en place sur les URL de liste et de détail d’un endpoint.

  • Les actions peuvent utiliser n’importe quelle méthode HTTP (GET, POST, PATCH, DELETE…).

Les actions servent à gérer des morceaux de logique métier, et ainsi à éviter aux applications clientes de les gérer, car elles pourraient les gérer différemment. Le fait d’ajouter un article au panier d’un utilisateur est un bon exemple d’action qui ne peut être réalisé que d’une seule façon, son traitement doit donc être réalisé par le serveur et non les applications clientes. Dans le chapitre suivant, vous découvrirez comment valider les données transmises par ces actions !

Exemple de certificat de réussite
Exemple de certificat de réussite