Fonctionnalité Formulaires et Favoris

Dans ce chapitre, vous apprendrez à gérer les requêtes POST des utilisateurs, à établir des relations plusieurs-à-plusieurs et à implémenter la fonctionnalité « articles favoris ».


Conception du modèle de données pour la fonctionnalité de favoris

Le cœur de la fonctionnalité de favoris est :Un utilisateur peut ajouter plusieurs articles à ses favoris, et un article peut être ajouté aux favoris par plusieurs utilisateurs.。

Il s'agit d'une relation plusieurs-à-plusieurs (ManyToMany) typique.

Nous n'avons pas besoin de créer un nouveau modèle — le modèle User de Django est intégré ; nous avons juste besoin d'ajouter un ManyToManyField pointant vers User dans le modèle Post.

Exemple

# Chemin du fichier : ajouter à la classe Post dans blog/models.py
from django.contrib.auth.models import Utilisateur

class Post(models.Model):
    # ... champs existants ...
    # Collector : ManyToManyField crée automatiquement une table intermédiaire
    favorites = models.ManyToManyField(
        Utilisateur,
        related_name='favorite_posts',  # Requête inverse : user.favorite_posts.all()
        verbose_name='collector',
        blank=True
    )

Après avoir modifié le modèle, générez et exécutez la migration :

(venv) $ python manage.py makemigrations
(venv) $ python manage.py migrate

Logique de vue pour l'ajout et le retrait des favoris

Exemple

# Chemin du fichier: blog/views.py (nouvellement ajouté)
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Post

# décorateur login_required : lorsqu'un utilisateur non authentifié accède à cette vue, rediriger automatiquement vers la page de connexion
@login_required
def toggle_favorite(request, pk):
    """Basculer le statut favori : favori → retirer des favoris, non favori → ajouter aux favoris"""
    post = get_object_or_404(Post, pk=pk)

    # Vérifier si l'utilisateur actuel a ajouté aux favoris
    if post.favorites.filter(id=request.user.id).exists():
        # Favori → Retirer des favoris
        post.favorites.remove(request.user)
        messages.info(request, f'Retiré des favoris "{post.title}"')
    else:
        # Non favori → Ajouter aux favoris
        post.favorites.add(request.user)
        messages.success(request, f'Ajouté aux favoris "{post.title}"')

    # Rediriger vers la page actuelle (HTTP_REFERER est l'URL de la page source)
    referer = request.META.get('HTTP_REFERER', '/')
    return redirect(referer)

Référence rapide des méthodes d'opération ManyToMany

MéthodesFonctionExemple
add(obj)Ajouter une associationpost.favorites.add(user)
remove(obj)Supprimer une associationpost.favorites.remove(user)
all()Interroger tous les objets associéspost.favorites.all()
filter(...)Filtrer avec des conditionspost.favorites.filter(id=1)
exists()Déterminer s'il existe un enregistrement correspondantpost.favorites.filter(id=1).exists()
clear()Effacer toutes les associationspost.favorites.clear()

Configurer la route des favoris

Exemple

# Chemin du fichier : ajouter à blog/urls.py
url = [
    # ... routes existantes ...
    path('post/<int:pk>/favorite/', views.toggle_favorite, name='toggle_favorite'),
]

Ajouter un bouton de favoris à la page de détail

Exemple

<!-- Chemin du fichier : blog/templates/blog/post_detail.html (modifié) -->
{% extends 'blog/base.html' %}

{% block title %}{{ post.title }} - Blog EXEMPLE{% endblock %}

{% block content %}
<article class="post-view">
    <span class="category-tag">{{ post.category }}</span>
    <div class="post-header">
        <h1>{{ post.title }}</h1>
        <!-- Bouton favori : visible uniquement par les utilisateurs connectés -->
        {% if user.is_authenticated %}
        <form method="post" action="{% url 'toggle_favorite' post.pk %}">
            {% csrf_token %}
            <button type="submit" class="fav-btn">
                {% if user in post.favorites.all %}
                    &#x2665;favorited
                {% else %}
♡ Favori
                {% endif %}
            </button>
        </forme>
        {% endif %}
    </div>
    <time>{{ post.created_at|date:"Y-m-d" }}</time>
    <div class="content">{{ post.content|safe }}</div>
    <a href="{% url 'index' %}" class="back-link">← Retour à l'accueil</a>
</article>
{% endblock %}

Ajoutez également un bouton de favoris aux cartes d'articles sur la page d'accueil :

Exemple

<!-- Ajouter un bouton favori à la carte d'article dans index.html (zone de pied de page) -->
<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>
        <div class="card-footer">
            <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
            {% if user.is_authenticated %}
            <form method="post" action="{% url 'toggle_favorite' post.pk %}" class="fav-form">
                {% csrf_token %}
                <button type="submit" class="fav-btn-small">
                    {% if user in post.favorites.all %}&#x2665;{% else %}♡{% endif %}
                </button>
            </forme>
            {% endif %}
        </div>
    </div>
</div>

Pourquoi le bouton favori utilise-t-il un formulaire au lieu d'une balise <a> ? Parce que l'opération de mise en favori modifie la base de données et est une requête POST. Selon les conventions REST, les opérations de lecture utilisent GET, les opérations d'écriture utilisent POST. Plus important encore, les requêtes POST transportentcsrf_token...pour empêcher des sites web malveillants de falsifier des requêtes.


Page de la liste des favoris

Afficher la liste des articles mis en favori pour les utilisateurs connectés.

Exemple

# Chemin du fichier: blog/views.py (nouvellement ajouté)
@login_required
def favorites(request):
    """Liste des favoris de l'utilisateur actuel"""
    # Utiliser related_name pour effectuer une requête inversée sur les articles mis en favori par l'utilisateur
    posts = request.user.favorite_posts.all().order_by('-created_at')
    return render(request, 'blog/favorites.html', {
        'posts': posts,
        'title': 'Mes favoris - Blog EXEMPLE'
    })

Ajoutez une route dans urls.py, et ajoutez un lien « Mes favoris » dans la barre de navigation.


Style du bouton des favoris

Exemple

/* Ajouter au style dans base.html */
.post-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
}

.fav-btn, .fav-btn-small {
    background: none;
    border: none;
    font-size: 18px;
    cursor: pointer;
    color: #e74c3c;
    padding: 4px 8px;
    transition: transform 0.2s;
}

.fav-btn:hover, .fav-btn-small:hover {
    transform: scale(1.2);
}

.fav-btn-small {
    font-size: 16px;
}

.fav-form {
    display: inline;
}

.card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 10px;
}

Résumé du chapitre

Dans ce chapitre, vous avez maîtrisé la gestion des interactions utilisateur dans Django : ManyToManyField pour établir des relations plusieurs-à-plusieurs, les requêtes POST pour gérer les formulaires, csrf_token pour la protection de sécurité, le décorateur login_required pour protéger les vues, et request.user pour déterminer l'identité de l'utilisateur actuel.

La fonctionnalité de favoris permet aux utilisateurs de marquer les articles qu'ils aiment et de les consulter dans leur liste de favoris personnelle.

autres extensions