Différenciez les informations de liste et de détail

Retournez plus d’informations dans le détail
Très souvent, les données en retour sont différentes selon qu’on consulte un endpoint de liste ou un endpoint de détail. En règle générale, les listes sont appelées pour être affichées. Lorsque l’utilisateur sélectionne un des éléments qui la composent, alors un autre appel est réalisé par l’application cliente afin d’obtenir plus d’informations.
Cela permet de réduire les temps de réponse en ne retournant que les informations utiles.
Dans notre cas, la liste des catégories est bien trop complète pour l’usage qui doit en être fait, et nous allons donc réduire les informations de liste, en conservant toutes nos données actuelles dans l'endpoint de détail d’une catégorie.
Améliorez le rendu du détail d’un endpoint
Améliorons notre endpoint de catégories en ne retournant que les informations minimales, dans le cas d’une liste, et détaillées, lorsque nous consultons une catégorie spécifique.
DRF nous permet au travers de ses viewsets de redéfinir la méthode get_serializer_class qui, elle, détermine le serializer à utiliser. Par défaut, le serializer retourné est celui défini sur l’attribut de classe serializer_class du viewset.
Lorsqu’une requête entre dans notre API, les viewsets définissent un attribut action qui correspond à l’action que l’application client est en train de réaliser. Elle peut être :
list : appel en
GETsur l’URL de liste ;retrieve : appel en
GETsur l’URL de détail (qui comporte alors un identifiant) ;create : appel en
POSTsur l’URL de liste ;update : appel en
PUTsur l’URL de détail ;partial_update : appel en
PATCHsur l’URL de détail ;destroy : appel en
DELETEsur l’URL de détail.class CategoryViewset(ReadOnlyModelViewSet): serializer_class = CategoryListSerializer # Ajoutons un attribut de classe qui nous permet de définir notre serializer de détail detail_serializer_class = CategoryDetailSerializer def get_queryset(self): return Category.objects.filter(active=True) def get_serializer_class(self): # Si l'action demandée est retrieve nous retournons le serializer de détail if self.action == 'retrieve': return self.detail_serializer_class return super().get_serializer_class()
Après avoir redémarré le serveur et avoir rafraîchit notre page, nous pourrons alors voir que les informations de la liste des catégories sont réduites, alors que le détail est plus complet.
C’est également le moment idéal pour mettre en place un test sur le détail d’une catégorie :
class TestCategory(ShopAPITestCase):
def test_detail(self):
# Nous utilisons l'url de détail
url_detail = reverse('category-detail',kwargs={'pk': self.category.pk})
response = self.client.get(url_detail)
# Nous vérifions également le status code de retour ainsi que les données reçues
self.assertEqual(response.status_code, 200)
excepted = {
'id': self.category.pk,
'name': self.category.name,
'date_created': self.format_datetime(self.category.date_created),
'date_updated': self.format_datetime(self.category.date_updated),
'products': self.get_product_detail_data(self.category.products.filter(active=True)),
}
self.assertEqual(excepted, response.json())Nos tests s’exécutent à présent en succès !
N’hésitez pas à dissocier les données retournées selon l’action demandée dès que cela est nécessaire. Il est également possible de définir un serializer dédié à la création ou à la modification, en se basant sur l’action souhaitée par l’application cliente.
Il peut aussi être intéressant de créer et utiliser un Mixin. Ceci pour permettre de mutualiser le code qui permet de définir le serializer à utiliser selon la liste et le détail. Cela évitera de réécrire du code et facilitera la maintenance.
Mais un Mixin, c’est quoi ?
Un Mixin est une classe qui est composée de méthodes concrètes, utilisée en héritage multiple pour apporter des fonctionnalités aux classes qui en héritent. C'est-à-dire qui est faite pour être héritée et non pas utilisée seule. Nous les verrons de plus près dans le prochain chapitre !
En résumé
L’action réalisée est définie dans l’attribut
actiondes viewsets, et permet de savoir précisément l’action en cours.Il est possible de définir un serializer différent pour chaque action.
Les mixins sont une bonne façon de gérer les différents serializers à utiliser, car cette opération est très courante.
Utiliser différents serializers contribue à améliorer les performances de l’API.
Dissocier les serializers de liste et de détail nous permettrait par exemple d’inclure les produits et articles dans le détail d’une catégorie. N’hésitez pas à expérimenter.
