Le texte ci-dessous est le dernier article de la série d'exploration des vues basées sur des classes (CBV) de Django, où nous présentons comment étendre ListView pour implémenter des fonctionnalités couramment utilisées en pratique, telles que la pagination, la recherche, et le tri. Si vous avez examiné les 7 parties précédentes, de la base des CBV à l'utilisation des Mixin, cet article résume comment traiter clairement les exigences générales rencontrées dans le développement web avec les CBV.

Pour accéder à l'article précédent, cliquez sur le lien ci-dessous !

Série d'exploration des vues basées sur des classes (CBV) ⑦ - Utilisation de Mixin et gestion des autorisations


“Étendez Django ListView pour implémenter des fonctionnalités de liste puissantes et améliorez l'expérience utilisateur !”


1. ListView, plus que de simples possibilités

ListView est une vue générique très utile pour afficher des listes de données. Cependant, dans les applications web réelles, en plus d'afficher simplement des listes, des fonctionnalités telles que la pagination pour gérer efficacement beaucoup de données, la recherche pour trouver rapidement les informations souhaitées, et le tri en fonction des préférences utilisateurs sont indispensables.

Ce chapitre aborde comment étendre ListView pour implémenter facilement et de manière élégante ces exigences pratiques en profitant des avantages des CBV.


2. Appliquer la pagination

Montrer une grande quantité de données en une seule fois n'est pas bénéfique pour l'expérience utilisateur. La pagination réduit la charge de chargement de l'utilisateur en divisant les données sur plusieurs pages, facilitant ainsi la recherche des informations souhaitées.

2.1 Configuration de base de la pagination

ListView fournit une fonctionnalité de pagination intégrée. Il suffit de définir la propriété paginate_by dans la classe de vue.

Exemple de views.py

from django.views.generic import ListView
from .models import Article

class ArticleListView(ListView):
    model = Article
    template_name = 'articles/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10 # Afficher 10 objets Article par page
  • paginate_by = 10 : En configurant comme ci-dessus, ListView divisera automatiquement les résultats en pages de 10 et transmettra les objets liés à la pagination (paginator, page_obj, is_paginated) au contexte du modèle.

2.2 Implémentation de l'UI de pagination dans le modèle

Dans le modèle, vous pouvez utiliser page_obj transmis au contexte pour afficher des liens de numéro de page et implémenter des boutons de page précédente/suivante.

Exemple de articles/article_list.html

<ul>
    {% for article in articles %}
        <li>{{ article.title }} - {{ article.created_at }}</li>
    {% empty %}
        <li>Aucun article publié pour le moment.</li>
    {% endfor %}
</ul>

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1">&laquo; Première</a>
            <a href="?page={{ page_obj.previous_page_number }}>Précédent</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} / {{ paginator.num_pages }}
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}>Suivant</a>
            <a href="?page={{ paginator.num_pages }}>Dernière &raquo;</a>
        {% endif %}
    </span>
</div>
  • page_obj: C'est l'objet qui contient les données de la page actuelle. Il fournit des propriétés telles que has_previous, has_next, previous_page_number, next_page_number, number, etc.
  • paginator: C'est l'objet qui contient des informations relatives à toutes les pages, y compris num_pages (nombre total de pages).

2.3 Maintenir les liens URL

Vous pouvez constater que la pagination utilise des paramètres de requête ?page=2. ListView reconnaît automatiquement ce paramètre pour afficher les données de la page correspondante.


Trouver rapidement les informations souhaitées sur un site Web est très important. Étendons ListView pour implémenter la fonction de recherche.

3.1 Définition du formulaire

Commençons par définir un simple formulaire pour recevoir les mots-clés de recherche.

Exemple de forms.py

from django import forms

class ArticleSearchForm(forms.Form):
    search_term = forms.CharField(label='Mot-clé', required=False)

3.2 Modification de ListView

Nous allons surcharger la méthode get_queryset() de ListView pour implémenter la fonction de recherche.

Exemple de views.py

from django.views.generic import ListView
from django.db.models import Q
from .models import Article
from .forms import ArticleSearchForm

class ArticleListView(ListView):
    model = Article
    template_name = 'articles/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10

    def get_queryset(self):
        queryset = super().get_queryset()
        self.form = ArticleSearchForm(self.request.GET) # Obtenir les données du formulaire à partir de la requête GET

        if self.form.is_valid():
            search_term = self.form.cleaned_data.get('search_term')
            if search_term:
                # Filtrer les articles dont le titre ou le contenu contient le mot-clé
                queryset = queryset.filter(
                    Q(title__icontains=search_term) | Q(content__icontains=search_term)
                )
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['search_form'] = self.form # Transmettre le formulaire au modèle
        return context
  • get_queryset(): En surchargant cette méthode, nous modifions le queryset par défaut.
  • Nous créons une instance de formulaire et, si elle passe la validation, nous obtenons le mot-clé depuis cleaned_data.
  • Nous utilisons l'objet Q pour filtrer les objets Article dont les titres (title) ou les contenus (content) contiennent le mot-clé (avec __icontains qui vérifie l'inclusion sans tenir compte de la casse).
  • get_context_data(): Nous ajoutons l'instance du formulaire au contexte afin qu'elle puisse être rendue dans le modèle.

3.3 Afficher le formulaire de recherche et maintenir les résultats dans le modèle

Ajoutons un formulaire de recherche au modèle et construisons l'URL de manière à permettre la pagination tout en maintenant les résultats de recherche.

articles/article_list.html

<form method="get">
    {{ search_form }}
    <button type="submit">Rechercher</button>
</form>

<ul>
    {% for article in articles %}
        <li>{{ article.title }} - {{ article.created_at }}</li>
    {% empty %}
        <li>Aucun résultat trouvé.</li>
    {% endfor %}
</ul>

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1&search_term={{ search_form.search_term.value|default:'' }}">&laquo; Première</a>
            <a href="?page={{ page_obj.previous_page_number }}&search_term={{ search_form.search_term.value|default:'' }}">Précédent</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} / {{ paginator.num_pages }}
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}&search_term={{ search_form.search_term.value|default:'' }}">Suivant</a>
            <a href="?page={{ paginator.num_pages }}&search_term={{ search_form.search_term.value|default:'' }}">Dernière &raquo;</a>
        {% endif %}
    </span>
</div>
  • Nous définissons method du formulaire sur get pour transmettre le mot-clé comme paramètre de requête dans l'URL.
  • Lors de la génération des liens de page, nous incluons le mot-clé actuel (search_form.search_term.value) dans les paramètres URL pour maintenir les résultats de recherche tout en paginant. Nous utilisons le filtre |default:'' pour afficher une chaîne vide si le mot-clé est manquant.

Exemple d'écran de ListView – Page de liste avec UI de recherche et de tri


4. Ajouter la fonction de tri

Ajoutons une fonctionnalité de tri pour permettre aux utilisateurs de voir les données selon leurs critères souhaités.

4.1 Ajout d'un formulaire de choix de tri (optionnel)

Vous pouvez également ajouter un formulaire déroulant pour sélectionner les critères de tri. Cela peut être géré simplement avec des paramètres URL. Nous expliquerons ici comment utiliser les paramètres URL.

4.2 Modification de ListView

Modifions la méthode get_queryset() pour appliquer order_by().

Exemple de views.py (avec fonction de recherche)

from django.views.generic import ListView
from django.db.models import Q
from .models import Article
from .forms import ArticleSearchForm

class ArticleListView(ListView):
    model = Article
    template_name = 'articles/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10

    def get_queryset(self):
        queryset = super().get_queryset()
        self.form = ArticleSearchForm(self.request.GET)
        ordering = self.request.GET.get('ordering', '-created_at') # Tri par défaut : du plus récent au plus ancien

        if self.form.is_valid():
            search_term = self.form.cleaned_data.get('search_term')
            if search_term:
                queryset = queryset.filter(
                    Q(title__icontains=search_term) | Q(content__icontains=search_term)
                )

        queryset = queryset.order_by(ordering)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['search_form'] = self.form
        context['ordering'] = self.request.GET.get('ordering', '-created_at') # Transmettre le critère de tri actuel au modèle
        return context
  • self.request.GET.get('ordering', '-created_at') : Obtient la valeur ordering à partir des paramètres de requête URL. S'il n'y a pas de valeur, le tri par défaut sera -created_at (du plus récent au plus ancien).
  • queryset = queryset.order_by(ordering) : Nous utilisons la valeur obtenue de ordering pour trier le queryset. En ajoutant - devant le nom du champ du modèle, nous trions dans l'ordre inverse.

4.3 Fournir des liens de tri dans le modèle

Dans le modèle, fournissons des liens pour trier chaque champ.

Exemple de articles/article_list.html

<form method="get">
    {{ search_form }}
    <button type="submit">Rechercher</button>
</form>

<table>
    <thead>
        <tr>
            <th><a href="?ordering=title&search_term={{ search_form.search_term.value|default:'' }}">Titre</a></th>
            <th><a href="?ordering=-created_at&search_term={{ search_form.search_term.value|default:'' }}">Date de rédaction</a></th>
            </tr>
    </thead>
    <tbody>
        {% for article in articles %}
            <tr>
                <td>{{ article.title }}</td>
                <td>{{ article.created_at }}</td>
            </tr>
        {% empty %}
            <tr><td colspan="2">Aucun résultat trouvé.</td></tr>
        {% endfor %}
    </tbody>
</table>

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">&laquo; Première</a>
            <a href="?page={{ page_obj.previous_page_number }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Précédent</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} / {{ paginator.num_pages }}
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Suivant</a>
            <a href="?page={{ paginator.num_pages }}&search_term={{ search_form.search_term.value|default:'' }}&ordering={{ ordering }}">Dernière &raquo;</a>
        {% endif %}
    </span>
</div>
  • Nous fournissons des liens avec les paramètres de tri (ordering) dans chaque en-tête de table.
  • Lors de la génération des liens de page, nous incluons non seulement le mot-clé de recherche actuel, mais aussi le critère de tri actuel (ordering) dans les paramètres URL pour maintenir l'état de tri tout en paginant.

Diagramme de processus de recherche/tris – Visualisation du flux d'extension de ListView


5. Combiner des fonctionnalités avec Mixin

En utilisant les Mixin dont nous avons appris l'utilisation précédemment, nous pouvons gérer plus proprement les fonctionnalités de pagination, recherche et tri. Par exemple, nous pouvons isoler la fonction de recherche dans un Mixin.

Exemple de mixins.py

from django.db.models import Q
from .forms import ArticleSearchForm

class ArticleSearchMixin:
    def get_queryset(self):
        queryset = super().get_queryset()
        self.form = ArticleSearchForm(self.request.GET)

        if self.form.is_valid():
            search_term = self.form.cleaned_data.get('search_term')
            if search_term:
                queryset = queryset.filter(
                    Q(title__icontains=search_term) | Q(content__icontains=search_term)
                )
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['search_form'] = getattr(self, 'form', ArticleSearchForm()) # Utiliser le formulaire par défaut s'il n'existe pas
        return context

Exemple de views.py (avec Mixin appliqué)

from django.views.generic import ListView
from .models import Article
from .mixins import ArticleSearchMixin

class ArticleListView(ArticleSearchMixin, ListView):
    model = Article
    template_name = 'articles/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10

    def get_queryset(self):
        queryset = super().get_queryset()
        ordering = self.request.GET.get('ordering', '-created_at')
        queryset = queryset.order_by(ordering)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['ordering'] = self.request.GET.get('ordering', '-created_at')
        return context
  • ArticleSearchMixin a été créé pour séparer la logique relative à la recherche.
  • ArticleListView hérite à la fois de ArticleSearchMixin et de ListView, obtenant ainsi toutes les fonctions de recherche et de liste.

6. Avantages et inconvénients des CBV (fin de la série)

Au cours de cette série d'exploration des CBV en 8 épisodes, nous avons examiné les raisons du passage des vues basées sur des fonctions (FBV) aux vues basées sur des classes (CBV), la structure et l'utilisation de base des CBV, l'utilisation de diverses vues génériques, ainsi que l'extension des fonctionnalités en utilisant des Mixins.

Principaux avantages des CBV :

  • Réutilisabilité du code: Grâce aux vues génériques et aux Mixins, nous pouvons réduire le code répétitif et favoriser un développement efficace.
  • Code structuré: Le code basé sur des classes isole clairement la logique, ce qui facilite la gestion.
  • Extensibilité: Grâce à l'héritage et aux Mixins, il est facile d'élargir les fonctionnalités existantes et d'ajouter de nouvelles fonctionnalités.
  • Maintenabilité: Le code étant modulé, il est plus facile à maintenir et réduit l'impact des modifications sur l'ensemble du code.
  • Augmentation de la productivité en développement: En utilisant différentes vues génériques et Mixins fournis par Django, le développement peut être effectué rapidement.

Limites et points à considérer des CBV :

  • Courbe d'apprentissage initiale: Comparé aux FBV, une compréhension de la structure basée sur des classes peut être nécessaire.
  • Héritage excessif: Une utilisation trop importante de Mixins peut rendre difficile la compréhension du flux du code. Il est important d'utiliser les Mixins à un niveau approprié.
  • Sur-ingénierie pour une logique simple: Pour des pages ou des fonctionnalités très simples, les FBV peuvent être plus succincts. Il est conseillé de choisir la méthode de vue appropriée selon la situation.

En conclusion, le CBV de Django est un outil très puissant et utile pour développer des applications web nécessitant des fonctionnalités complexes et variées. Si vous comprenez et utilisez correctement les avantages des CBV, vous pouvez écrire un code plus efficace et facile à maintenir.

Merci d'avoir lu la série d'exploration des vues basées sur des classes (CBV) jusqu'au bout ! J'espère que cette série vous aura aidé à comprendre le CBV de Django et à l'appliquer dans vos projets réels.


Revoir l'article précédent

  1. Série d'exploration des vues basées sur des classes (CBV) #1 – Les raisons du passage de FBV à CBV et l'état d'esprit du développeur
  2. Série d'exploration des vues basées sur des classes (CBV) #2 – Compréhension des classes de vue de base de Django
  3. Série d'exploration des vues basées sur des classes (CBV) #3 – Traitement simple des formulaires avec FormView
  4. Série d'exploration des vues basées sur des classes (CBV) #4 – Utilisation de ListView et DetailView
  5. Série d'exploration des vues basées sur des classes (CBV) #5 – Implémentation du CRUD avec CreateView, UpdateView, et DeleteView
  6. Série d'exploration des vues basées sur des classes (CBV) #6 – Utilisation de TemplateView et RedirectView
  7. Série d'exploration des vues basées sur des classes (CBV) #7 – Utilisation de Mixin et gestion des autorisations

“Améliorez les fonctionnalités de votre liste en étendant ListView, et

maîtrisez le CBV pour élever votre niveau de développement Django !”