124 lines
5.4 KiB
TeX
124 lines
5.4 KiB
TeX
\chapter{Filtres}
|
|
|
|
A ce stade, nous pouvons juste récupérer des informations présentes dans notre base de données, mais à part les parcourir, il est difficile d'en faire quelque chose.
|
|
|
|
Il est possible de jouer avec les URLs en définissant une nouvelle route ou avec les paramètres de l'URL, ce qui demanderait alors de programmer chaque cas possible - sans que le consommateur ne puisse les déduire
|
|
lui-même.
|
|
Une solution élégante consiste à autoriser le consommateur à filtrer les données, directement au niveau de l'API.
|
|
Ceci peut être fait.
|
|
Il existe deux manières de restreindre l'ensemble des résultats retournés:
|
|
|
|
\begin{enumerate}
|
|
\item
|
|
Soit au travers d'une recherche, qui permet d'effectuer une recherche textuelle, globale et par ensemble à un ensemble de champs,
|
|
\item
|
|
Soit au travers d'un filtre, ce qui permet de spécifier une valeur précise à rechercher.
|
|
\end{enumerate}
|
|
|
|
Dans notre exemple, la première possibilité sera utile pour rechercher une personne répondant à un ensemble de critères.
|
|
Typiquement, \texttt{/api/v1/people/?search=raymond\ bond} ne nous donnera aucun résultat, alors que \texttt{/api/v1/people/?search=james\ bond} nous donnera le célèbre agent secret (qui a bien entendu un contrat chez nous\ldots\hspace{0pt}).
|
|
|
|
Le second cas permettra par contre de préciser que nous souhaitons disposer de toutes les personnes dont le contrat est ultérieur à une date particulière.
|
|
|
|
Utiliser ces deux mécanismes permet, pour Django-Rest-Framework, de proposer immédiatement les champs, et donc d'informer le consommateur des possibilités :
|
|
|
|
\includegraphics{images/rest/drf-filters-and-searches.png}
|
|
|
|
|
|
\section{Recherches}
|
|
La fonction de recherche est déjà implémentée au niveau de Django-Rest-Framework, et aucune dépendance supplémentaire n'est nécessaire.
|
|
Au niveau du \texttt{viewset}, il suffit d'ajouter deux informations:
|
|
|
|
\begin{minted}{python}
|
|
...
|
|
from rest_framework import filters, viewsets
|
|
...
|
|
|
|
class PeopleViewSet(viewsets.ModelViewSet):
|
|
...
|
|
filter_backends = [filters.SearchFilter]
|
|
search_fields = ["last_name", "first_name"]
|
|
...
|
|
\end{minted}
|
|
|
|
\subsection{Filtres}
|
|
|
|
Nous commençons par installer le paquet django-filter \url{https://www.django-rest-framework.org/api-guide/filtering/\#djangofilterbackend})
|
|
et nous l'ajoutons parmi les applications installées:
|
|
|
|
\begin{verbatim}
|
|
pip install django-filter
|
|
Collecting django-filter
|
|
Downloading django_filter-2.4.0-py3-none-any.whl (73 kB)
|
|
| 73 kB 2.6 MB/s
|
|
Requirement already satisfied: Django>=2.2 in c:\users\fred\sources\.venvs\r
|
|
ps\lib\site-packages (from django-filter) (3.1.7)
|
|
Requirement already satisfied: asgiref<4,>=3.2.10 in c:\users\fred\sources
|
|
\.venvs\rps\lib\site-packages (from Django>=2.2->django-filter) (3.3.1)
|
|
Requirement already satisfied: sqlparse>=0.2.2 in c:\users\fred\sources\.
|
|
venvs\rps\lib\site-packages (from Django>=2.2->django-filter) (0.4.1)
|
|
Requirement already satisfied: pytz in c:\users\fred\sources\.venvs\rps\lib
|
|
\site-packages (from Django>=2.2->django-filter) (2021.1)
|
|
Installing collected packages: django-filter
|
|
Successfully installed django-filter-2.4.0
|
|
\end{verbatim}
|
|
|
|
Une fois l'installation réalisée, il reste deux choses à faire :
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
Ajouter \texttt{django\_filters} parmi les applications installées :
|
|
\item
|
|
Configurer la clé \texttt{DEFAULT\_FILTER\_BACKENDS} à la valeur \texttt{{[}\textquotesingle{}django\_filters.rest\_framework.DjangoFilterBackend\textquotesingle{}{]}}.
|
|
\end{enumerate}
|
|
|
|
Vous avez suivi les étapes ci-dessus, il suffit d'adapter le fichier \texttt{settings.py} de la manière suivante :
|
|
|
|
\begin{minted}{python}
|
|
REST_FRAMEWORK = {
|
|
'DEFAULT_PAGINATION_CLASS':
|
|
'rest_framework.pagination.PageNumberPagination',
|
|
'PAGE_SIZE': 10,
|
|
'DEFAULT_FILTER_BACKENDS':
|
|
['django_filters.rest_framework.DjangoFilterBackend']
|
|
}
|
|
\end{minted}
|
|
|
|
|
|
Au niveau du viewset, il convient d'ajouter ceci:
|
|
|
|
\begin{minted}{python}
|
|
...
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from rest_framework import viewsets
|
|
...
|
|
|
|
class PeopleViewSet(viewsets.ModelViewSet):
|
|
...
|
|
filter_backends = [DjangoFilterBackend]
|
|
filterset_fields = ('last_name',)
|
|
...
|
|
\end{minted}
|
|
|
|
|
|
A ce stade, nous avons deux problèmes:
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
Le champ que nous avons défini au niveau de la propriété \texttt{filterset\_fields} exige une correspondance exacte.
|
|
Ainsi, \texttt{/api/v1/people/?last\_name=Bon} ne retourne rien, alors que \texttt{/api/v1/people/?last\_name=Bond} nous donnera notre agent
|
|
secret préféré.
|
|
\item
|
|
Il n'est pas possible d'aller appliquer un critère de sélection sur la propriété d'une relation.
|
|
Notre exemple proposant rechercher uniquement les relations dans le futur (ou dans le passé) tombe à l'eau.
|
|
\end{enumerate}
|
|
|
|
Pour ces deux points, nous allons définir un nouveau filtre, en surchargeant une nouvelle classe dont la classe mère serait de type \texttt{django\_filters.FilterSet}.
|
|
|
|
TO BE CONTINUED.
|
|
|
|
A noter qu'il existe un paquet {[}Django-Rest-Framework-filters{]}(\url{https://github.com/philipn/django-rest-framework-filters}), mais il est déprécié depuis Django 3.0, puisqu'il se base sur \texttt{django.utils.six} qui n'existe à présent plus.
|
|
Il faut donc le faire à la main (ou patcher le paquet\ldots\hspace{0pt}).
|