Django Rest Framework : gérer les relations many to many “through”.
https://gist.github.com/06d6e52d102bc4d93ffa
Le model Service représente une prestation, le model Option définit un ensemble de Service, qui forment ainsi l'option. Il nous faut le model OptionService car le prix d'une prestation dépend de l'option à laquelle elle est rattachée.
L'api que je souhaite exposer est donc la suivante :
https://gist.github.com/97d81fbe526384ea9c14
Commençons par créer le serializer qui pose le moins de problème :
https://gist.github.com/0f6295cfca59be7a4160
Nous utilisons ici des HyperlinkedModelSerializer pour afficher l'url des objets plutĂ´t que des foreign_key afin de rester le plus unitaire possible.
Maintenant que le serializer est prĂŞt, nous allons utiliser une ModelViewSet et un DefaultRouter pour pouvoir rapidement commencer Ă naviguer dans notre api :
https://gist.github.com/70609d53e735af72db26
https://gist.github.com/a234445e3ee636fa88de
Je me suis permis de créer quelques objet Service via l'admin Django pour avoir quelques données. Nous pouvons maintenant accéder à la liste des services via l'api :
https://gist.github.com/153a7780612627ba5d69
Ca c'était pour la partie facile ! Maintenant nous allons entrer dans le vif du sujet, la gestion de la relation many to many entre Option et Service via OptionService.
drf-nested-routers Ă la rescousse
Django REST Framework ne gère pas nativement le genre d'url que l'on veut obtenir, à savoir une url du style /options/1/services/2/ pour afficher l'objet OptionService qui relie l'option 1 avec le service 2.
Pour ce faire, nous allons avoir besoin du package drf-nested-routers. Une petite commande pip install drf-nested-routers suffira Ă l'installer.
Ce package va nous aider pour les routes mais ne gère pas correctement les relations avec les HyperlinkedRelatedField proposé par Django REST Framework. Nous allons donc créer notre propre HyperlinkedNestedRelatedField dérivé de HyperlinkedRelatedField. Je vous propose ce gist :
https://gist.github.com/39296bfe6db5925be663
Vous pouvez créer cette classe dans un module relations.py par exemple.
Nous allons grâce à cette classe pouvoir afficher la liste des services liés à une option via la classe OptionService sous la forme : /options/1/services/2/.
Nous sommes maintenant équipés pour créer les serializers et les vues qu'il nous manque. Commençons par les serializers:
https://gist.github.com/4f288d598f6ef7039cfd
Le paramètre view_name='optionservice-detail'fait référence au nom de la route que va générer drf-nested-router. Nous verrons ça juste après. Car avant de créer les routes, nous avons besoin de créer les vues qu'il nous manque :
https://gist.github.com/8f43d32bddabad1f196b
Ne prenez pas peur devant l'ampleur de ce gist. En fait il s'agit beaucoup de copier-coller depuis les classes de bases de Django REST Framework. Concentrons nous sur les modifications apportées :
dans la vue OptionServiceViewSet j'ai ajouté une variable lookup_url_kwargs au pluriel, il existe aussi une variable lookup_url_kwarg au singulier qui permet à Django REST Framework de faire quelques vérifications quant à la structure de l'URL. Comme ici nous avons plusieurs arguments dans l'url, la variable au pluriel nous permet d'itérer sur le tableau d'argument pour vérifier que les arguments figurent bien dans l'url (c'est l'objectif du for lookup_url_kwarg in self.lookup_url_kwargs dans la méthode get_object)
la méthode get_object() a été modifié pour filtrer en fonction des arguments reçu, à savoir option_pk et pk qui représente en fait ici l'id du service et non celui de l'objet OptionService !
Pour le reste les docstring sont suffisantes pour comprendre.
Dernière étape : configurer les routes grâce à drf-nested-routers. Ajouter dans votre fichier urls.py la configuration suivante :
https://gist.github.com/2c5a7e958f26cec28640
Nous avons maintenant la structure d'api que nous recherchions :) .


















