153 lines
5.4 KiB
TeX
153 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}).
|