# Documentation Django 5.2.8

# Introduction aux vues fondées sur les classes

Les vues fondées sur les classes sont une autre manière d’implémenter les vues par des objets Python au lieu de fonctions. Elles ne remplacent pas les vues fondées sur les fonction, mais présentent certaines différences et avantages comparées aux vues fondées sur les fonctions :

- L’organisation du code en lien avec les méthodes HTTP spécifiques (`GET`, `POST`, etc.) peut se faire par des méthodes séparées au lieu d’utiliser un branchement conditionnel.

- Des techniques orientées objet comme les « mixins » (héritage multiple) peuvent être utilisées pour factoriser le code en composants réutilisables.

## Liaisons et historique des vues génériques, des vues fondées sur les classes et de celles fondées sur les fonctions

Au départ, il n’y avait que le contrat de la vue fonction à laquelle Django transmet un requête `HttpRequest` et de laquelle il s’attend à recevoir une réponse `HttpResponse`. C’était ce que Django lui-même pratiquait.

Assez rapidement, on a reconnu des idiomes et des modèles courants dans le développement des vues. Les vues génériques fondées sur les fonctions ont été introduites pour abstraire ces pratiques et faciliter le développement de vues pour ces cas courants.

Le problème avec les vues génériques fondées sur les fonctions, c’est qu’elles recouvrent bien les cas simples, mais qu’il n’est pas possible de les étendre ou de les personnaliser au-delà de certaines options de configurations, limitant ainsi leur utilité dans bien des applications du monde réel.

Les vues génériques fondées sur les classes ont été crées avec les mêmes objectifs que celles fondées sur les fonctions, pour faciliter le développement de vues. Cependant, la manière d’implémenter cette solution par l’utilisation de mixins fournit un ensemble d’outils aboutissant à des vues génériques fondées sur les classes bien plus souples et extensibles que leur contrepartie fondées sur les fonctions.

Si vous avez essayé d’utiliser les vues génériques fondées sur les fonctions par le passé et que vous les avez trouvées trop limitées, vous ne devriez pas considérer les vues génériques fondées sur les classes comme des équivalents, mais plutôt comme une nouvelle approche pour résoudre les problèmes d’origine que les vues génériques étaient censées résoudre.

L’ensemble des classes de base et des mixins que Django utilise pour construire les vues génériques fondées sur les classes sont conçues pour une souplesse maximale, et présentent ainsi de nombreux points d’accrochage sous les forme d’implémentation de méthodes par défaut et d’attributs qui ne vous concerneront probablement pas dans les cas d’utilisation les plus simples. Par exemple, au lieu de se limiter à un attribut de classe pour `form_class`, l’implémentation utilise une méthode `get_form` appelant à son tour la méthode `get_form_class` qui dans son implémentation par défaut renvoie l’attribut `form_class` de la classe. Cela vous donne plusieurs options pour indiquer le formulaire à utiliser, que ce soit par un attribut ou par l’appel dynamique à une méthode que l’on peut surcharger. Ces options paraissent ajouter une complexité un peu absurde dans des situations simples, mais sans elles, les scénarios plus compliqués seraient limités.

## Utilisation des vues fondées sur les classes

À la base, une vue fondée sur les classes permet de répondre à différentes méthodes de requête HTTP par différentes méthodes d’une classe plutôt que de faire appel à des branchements conditionnels dans une unique vue de type fonction.

Alors que le code pour gérer la méthode HTTP `GET` dans une fonction de vue ressemble à quelque chose comme cela :

    from django.http import HttpResponse

    def my_view(request):
        if request.method == "GET":
            # <view logic>
            return HttpResponse("result")

Dans une vue fondée sur les classes, cela devient :

    from django.http import HttpResponse
    from django.views import View

    class MyView(View):
        def get(self, request):
            # <view logic>
            return HttpResponse("result")

Comme le résolveur d’URL de Django est conçu pour envoyer la requête et ses paramètres associés à une fonction exécutable et non pas à une classe, les vues fondées sur les classes possèdent une méthode de classe `as_view()` qui renvoie une fonction pouvant être appelée lorsqu’une requête arrive avec une URL correspondant au motif associé. La fonction crée une instance de la classe, appelle `setup()` pour initialiser ses attributs, puis appelle sa méthode `dispatch()`. `dispatch` examine la requête pour déterminer s’il s’agit d’un `GET`, d’un `POST`, etc. et relaie la requête à une méthode correspondante si elle existe, ou génère l’erreur `HttpResponseNotAllowed` dans le cas contraire :

    # urls.py
    from django.urls import path
    from myapp.views import MyView

    urlpatterns = [
        path("about/", MyView.as_view()),
    ]

Il vaut la peine de relever que ce qui est renvoyé par la méthode est identique à ce qui est renvoyé par une vue de type fonction, en l’occurrence une instance de `HttpResponse`. Cela signifie que les raccourcis http ou les objets `TemplateResponse` peuvent tout à fait être utilisés dans une vue fondée sur les classes.

Même si une vue fondée sur les classes minimale ne nécessite aucun attribut de classe pour faire son travail, les attributs de classe sont utiles dans beaucoup d’architectures basées sur les classes et il existe deux façons de configurer ou de définir des attributs de classe.

La première est la manière standard de Python d’hériter ou de surcharger des attributs et des méthodes dans les sous-classes. Ainsi si la classe parente avait un attribut `greeting` comme ceci :

    from django.http import HttpResponse
    from django.views import View

    class GreetingView(View):
        greeting = "Good Day"

        def get(self, request):
            return HttpResponse(self.greeting)

Vous pouvez surcharger cela dans une sous-classe :

    class MorningGreetingView(GreetingView):
        greeting = "Morning to ya"

Une autre option est de configurer des attributs de classe comme paramètres nommés dans l’appel à `as_view()` dans la configuration d’URL :

    urlpatterns = [
        path("about/", GreetingView.as_view(greeting="G'day")),
    ]

Note

Alors qu’une instance de classe est créée pour chaque requête qu’elle doit traiter, les attributs de classe définis par le point d’entrée `as_view()` ne sont configurés qu’une seule fois au moment de l’importation de la configuration d’URL.

## Utilisation de mixins

Les mixins sont une forme d’héritage multiple où les comportements et les attributs de plusieurs classes parentes peuvent être combinés.

Par exemple, dans les vues génériques fondées sur les classes, il existe un mixin nommé `TemplateResponseMixin` dont le rôle principal est de définir la méthode `render_to_response()`. Lorsqu’il est combiné avec le comportement de la classe de base `View`, le résultat est une classe `TemplateView` qui distribue les requêtes vers les méthodes correspondantes (un comportement défini dans la classe de base `View`), et qui possède une méthode `render_to_response()` utilisant un attribut `template_name` pour renvoyer un objet `TemplateResponse` (comportement défini dans `TemplateResponseMixin`).

Les mixins sont une très bonne façon de réutiliser du code entre plusieurs classes, mais il y a aussi un revers de médaille. Plus votre code est réparti entre des mixins, plus il sera difficile de lire une classe enfant et de comprendre exactement ce qu’elle fait, et plus il sera difficile de savoir quelle méthode de quel mixin surcharger si vous héritez d’une structure avec une profonde arborescence d’héritage.

Notez également que vous ne pouvez hériter que d’une vue générique, c’est-à-dire qu’une seule classe parente peut hériter de `View` et toutes les autres classes (le cas échéant) doivent être des mixins. Si vous essayez d’hériter de plus d’une classe héritant de `View`, par exemple pour utiliser un formulaire au sommet d’une liste et combiner `ProcessFormView` avec `ListView`, cela ne marchera pas comme espéré.

## Gestion de formulaires avec les vues fondées sur les classes

Une vue de type fonction qui gère des formulaires peut ressembler à ceci :

    from django.http import HttpResponseRedirect
    from django.shortcuts import render

    from .forms import MyForm

    def myview(request):
        if request.method == "POST":
            form = MyForm(request.POST)
            if form.is_valid():
                # <process form cleaned data>
                return HttpResponseRedirect("/success/")
        else:
            form = MyForm(initial={"key": "value"})

        return render(request, "form_template.html", {"form": form})

Une vue équivalente fondée sur les classes pourrait ressembler à ceci :

    from django.http import HttpResponseRedirect
    from django.shortcuts import render
    from django.views import View

    from .forms import MyForm

    class MyFormView(View):
        form_class = MyForm
        initial = {"key": "value"}
        template_name = "form_template.html"

        def get(self, request, *args, **kwargs):
            form = self.form_class(initial=self.initial)
            return render(request, self.template_name, {"form": form})

        def post(self, request, *args, **kwargs):
            form = self.form_class(request.POST)
            if form.is_valid():
                # <process form cleaned data>
                return HttpResponseRedirect("/success/")

            return render(request, self.template_name, {"form": form})

Il s’agit d’un cas minimal, mais vous pouvez voir que vous aurez ensuite l’option de personnaliser cette vue en surchargeant l’un des ses attributs de classe, par ex. `form_class`, à travers la configuration d’URL ou par l’héritage et la surcharge de l’une ou plusieurs de ses méthodes (les deux pouvant être combinés).

## Décoration des vues fondées sur les classes

L’extension des vues fondées sur les classes n’est pas limitée aux mixins. Il est aussi possible d’utiliser des décorateurs. Comme les vues fondées sur les classes ne sont pas des fonctions, leur décoration fonctionne différemment selon que vous utilisez `as_view()` ou que vous créez une sous-classe.

### Décoration dans la configuration d’URL

Il est possible d’adapter les vues fondées sur les classes en décorant le résultat de la méthode `as_view()`. L’endroit le plus commode pour le faire est la configuration d’URL dans laquelle la vue est déployée :

    from django.contrib.auth.decorators import login_required, permission_required
    from django.views.generic import TemplateView

    from .views import VoteView

    urlpatterns = [
        path("about/", login_required(TemplateView.as_view(template_name="secret.html"))),
        path("vote/", permission_required("polls.can_vote")(VoteView.as_view())),
    ]

Cette approche applique le décorateur au niveau de l’instance. Si vous souhaitez que chaque instance d’une vue soit systématiquement décorée, vous devez suivre une autre approche.

### Décoration de la classe

Pour décorer toutes les instances d’une vue fondée sur les classes, vous devez décorer la définition de classe elle-même. À cet effet, appliquez le décorateur à la méthode `dispatch()` de la classe.

Une méthode de classe n’est pas totalement identique à une fonction autonome, il n’est donc pas possible de simplement appliquer un décorateur de fonction à la méthode, il faut préalablement le transformer en un décorateur de méthode. Le décorateur `method_decorator` transforme un décorateur de fonction en un décorateur de méthode afin qu’il puisse être utilisé sur une méthode d’instance. Par exemple :

    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    from django.views.generic import TemplateView

    class ProtectedView(TemplateView):
        template_name = "secret.html"

        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super().dispatch(*args, **kwargs)

Ou plus succintement, vous pouvez plutôt décorer la classe et passer le nom de la méthode à décorer dans le paramètre nommé `name`:

    @method_decorator(login_required, name="dispatch")
    class ProtectedView(TemplateView):
        template_name = "secret.html"

Si un ensemble de décorateurs sont utilisés à plusieurs endroits, vous pouvez définir une liste ou un tuple de décorateurs et l’utiliser au lieu d’appeler plusieurs fois `method_decorator()`. Ces deux classes sont équivalentes :

    decorators = [never_cache, login_required]

    @method_decorator(decorators, name="dispatch")
    class ProtectedView(TemplateView):
        template_name = "secret.html"

    @method_decorator(never_cache, name="dispatch")
    @method_decorator(login_required, name="dispatch")
    class ProtectedView(TemplateView):
        template_name = "secret.html"

Les décorateurs traitent une requête dans l’ordre où ils sont placés comme décorateurs. Dans l’exemple, `never_cache()` traitera la requête avant `login_required()`.

Dans cet exemple, chaque instance de `ProtectedView` recevra une protection de connexion. Ces exemples utilisent `login_required`, mais le même comportement peut être obtenu en utilisant `LoginRequiredMixin`.

Note

`method_decorator` transmet `*args` et `**kwargs` comme paramètres à la méthode décorée de la classe. Si votre méthode n’accepte pas un jeu de paramètres compatible, cela générera une exception `TypeError`.