Filtrage par catégorie et recherche par mot-clé

Dans ce chapitre, vous apprendrez à gérer les paramètres de requête URL, à implémenter une navigation par filtrage de catégorie, et une fonctionnalité de recherche par mot-clé.


Paramètres de requête URL

Les paramètres de requête sont la partie de l'URL après le point d'interrogation, tels que/?category=django&q=入门。

Dans les fonctions de vue Django, utilisezrequest.GETRécupérez ces paramètres.

Exemple

def index(request):
    # request.GET est un objet de type dictionnaire
    # .get('key', 'Default value') récupère en toute sécurité le paramètre, renvoie la valeur par défaut s'il n'existe pas
    category_slug = request.GET.get('category', '')   # Filtre de catégorie
    keyword = request.GET.get('q', '')                 # Mots-clés de recherche
    print(f'Catégorie : {category_slug}, Recherche : {keyword}')

Filtrage chaîné ORM et objets Q

Le filtrage et la recherche nécessitent d'assembler dynamiquement les conditions de requête.

Exemple

# Chemin du fichier : blog/views.py, mettez à jour la fonction index
from django.shortcuts import render
from django.db.models import Q           # Les objets Q sont utilisés pour combiner des conditions de requête complexes.
from .models import Post, Category

def index(request):
    """Page d'accueil du blog : afficher la liste des articles, prendre en charge le filtrage par catégorie + la recherche par mot-clé"""
    posts = Post.objects.all().order_by('-created_at')

    # 1. Obtenir les paramètres de requête
    category_slug = request.GET.get('category', '')
    keyword = request.GET.get('q', '')

    # 2. Filtrer par catégorie
    if category_slug:
        # category__slug : accéder au champ slug de la table des catégories via la clé étrangère (double tiret bas)
        posts = posts.filter(category__slug=category_slug)

    # 3. Rechercher par mot-clé (titre ou résumé)
    if keyword:
        # Q(title__icontains=...) | Q(summary__icontains=...): combinaison OU
        # icontains : requête contains insensible à la casse
        posts = posts.filter(
            Q(title__icontains=keyword) | Q(summary__icontains=keyword)
        )

    # 4. Récupérer toutes les catégories (pour le rendu de la navigation de filtres)
    categories = Category.objects.all()

    context = {
        'posts': posts,
        'categories': categories,
        'category_slug': category_slug,   # Passé au template pour mettre en évidence la sélection actuelle
        'keyword': keyword,               # Renvoyer les mots-clés de recherche
        'title': 'EXAMPLE Blog - Accueil',
    }
    return render(request, 'blog/index.html', context)

Explication des méthodes de requête ORM

MéthodesSignificationExemple
field__exactCorrespondance exactetitle__exact='Django'
field__iexactCorrespondance exacte insensible à la cassetitle__iexact='django'
field__containsContient (sensible à la casse)title__contains='入门'
field__icontainsContient (insensible à la casse)title__icontains='django'
field__gte / lteSupérieur ou égal à / Inférieur ou égal àcreated_at__gte='2024-01-01'
field__in=[...]Dans la listecategory__in=[1, 2, 3]

Double underscore__est une convention fondamentale de l'ORM Django : utilisée pour accéder aux champs des modèles associés, ou pour spécifier comment interroger les champs. Par exemplecategory__slugreprésente la traversée de la clé étrangère vers le champ slug de la table Category.


Mettre à jour le template de la page d'accueil

Ajouter une navigation de filtrage par catégorie et une zone de recherche au-dessus de la liste des articles.

Exemple

<!-- Chemin du fichier : blog/templates/blog/index.html -->
{% extends 'blog/base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block content %}
<h2 class="section-title">Derniers articles</h2>

<!-- Boîte de recherche -->
<div class="search-bar">
    <!-- Formulaire method="get":les paramètres soumis apparaissent dans l'URL(/search/?q=xxx) -->
    <form method="get" action="{% url 'index' %}">
        <input
           type="text"
           name="q"
           value="{{ keyword }}"
           placeholder="Rechercher des titres ou des résumés d'articles..."
           class="search-input"
       />
        {% if keyword %}
        <a href="{% url 'index' %}" class="clear-btn">✕</a>
        {% endif %}
    </forme>
</div>

<!-- Navigation de filtrage par catégorie -->
<div class="category-bar">
    <!-- Bouton « Toutes » : sans paramètre de catégorie -->
    <a href="{% url 'index' %}"
      class="{% if not category_slug %}active{% endif %}">Toutes</a>

    {% for cat in categories %}
    <a href="{% url 'index' %}?category={{ cat.slug }}{% if keyword %}&q={{ keyword }}{% endif %}"
      class="{% if category_slug == cat.slug %}active{% endif %}">
        {{ cat.name }}
    </a>
    {% endfor %}
</div>

<!-- Statistiques des résultats de filtrage -->
<p class="result-info">
Total {{ posts.count }} articles
{% if keyword %}, recherche de "{{ keyword }}"{% endif %}
</p>

{% if posts %}
    <div class="article-grid">
        {% for post in posts %}
        <a href="{% url 'post_detail' post.pk %}" class="card-link">
            <div class="article-card">
                <div class="card-content">
                    <span class="card-category">{{ post.category.name }}</span>
                    <h3>{{ post.title }}</h3>
                    <p>{{ post.summary|truncatechars:80 }}</p>
                    <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
                </div>
            </div>
        </a>
        {% endfor %}
    </div>
{% else %}
    <p class="empty-tip">Aucun article correspondant trouvé</p>
{% endif %}
{% endblock %}

Styles de navigation de filtre et de champ de recherche

Exemple

/* Ajouter au style dans base.html */
.search-bar {
    margin-bottom: 20px;
}

.search-input {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid #eee;
    border-radius: 8px;
    font-size: 15px;
    outline: none;
    transition: border-color 0.2s;
}

.search-input:focus {
    border-color: #2c3e50;
}

.clear-btn {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: #999;
    text-decoration: none;
    font-size: 18px;
}

.category-bar {
    display: flex;
    gap: 10px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.category-bar a {
    padding: 6px 16px;
    border: 1px solid #ddd;
    border-radius: 20px;
    text-decoration: none;
    color: #333;
    font-size: 14px;
    transition: all 0.2s;
}

.category-bar a.active {
    background: #2c3e50;
    color: #fff;
    border-color: #2c3e50;
}

.category-bar a:hover {
    border-color: #2c3e50;
}

.result-info {
    color: #999;
    font-size: 14px;
    margin-bottom: 16px;
}

.card-link {
    text-decoration: none;
    color: inherit;
}

Les liens des boutons de catégorie conservent égalementcategoryetkeywordParamètres :?category={{ cat.slug }}&q={{ keyword }}. Ainsi, après qu'un utilisateur a d'abord effectué une recherche puis cliqué sur une catégorie, la condition de recherche n'est pas perdue, et les deux conditions prennent effet simultanément.


Mise en pratique : tester le filtrage et la recherche

Démarrez le serveur et vérifiez les points suivants :

  • Cliquez sur le bouton de catégorie pour afficher uniquement les articles de la catégorie correspondante.
  • Saisissez des mots-clés pour filtrer les titres et les résumés en temps réel.
  • Catégorie + recherche : les résultats filtrés satisfont les deux conditions simultanément
  • Le bouton "Tous" efface le filtre de catégorie et rétablit l'affichage de tous les articles.

Résumé du chapitre

Dans ce chapitre, vous avez maîtrisé les méthodes de gestion des paramètres de requête URL dans Django : utiliser request.GET.get() pour obtenir des paramètres, le filtrage en chaîne ORM + les objets Q pour implémenter des requêtes complexes, et concaténer dynamiquement les paramètres URL dans les templates pour implémenter des liens de filtre.

Le filtrage par catégorie et la fonctionnalité de recherche permettent aux utilisateurs de trouver rapidement des articles qui les intéressent.

autres extensions