Utilisation de mixins avec les vues fondées sur les classes¶
Prudence
Ceci est un sujet avancé. Nous suggérons d’avoir une bonne connaissance préalable des vues Django fondées sur les classes avant d’explorer ces techniques.
Les vues fondées sur les classes intégrées dans Django fournissent de nombreuses fonctionnalités, mais certaines d’entre elles pourraient être utiles de manière indépendante. Par exemple, si l’on écrit une vue qui utilise un gabarit pour produire une réponse HTTP, mais sans utiliser TemplateView
; ou encore si l’on veut utiliser un gabarit seulement pour une requête POST
et faire quelque chose de totalement différent pour les requêtes GET
. Même si on pourrait utiliser directement TemplateResponse
, on se retrouverait avec pas mal de code dupliqué.
C’est pour cette raison que Django fournit aussi un certain nombre de mixins englobant des fonctionnalités de manière distincte. Le rendu de gabarits, par exemple, est isolé dans la classe TemplateResponseMixin
. La documentation de référence de Django contient une documentation complète de toutes les classes mixins.
Contexte et réponses par gabarit¶
Deux classes mixins centrales sont à disposition pour aider à présenter une interface cohérente pour travailler avec les gabarits dans les vues fondées sur les classes.
TemplateResponseMixin
Chaque vue intégrée renvoyant une réponse
TemplateResponse
appelle la méthoderender_to_response()
fournie parTemplateResponseMixin
. Dans la plupart des cas, cet appel se fait automatiquement (par exemple, elle est appelée par la méthodeget()
aussi bien deTemplateView
que deDetailView
). De même, il est peu probable que vous ayez besoin de la surcharger, bien que cela peut se révéler utile si vous voulez que la réponse renvoie du contenu non rendu par un gabarit Django. Vous pouvez trouver un exemple d’un tel usage dans l’exemple JSONResponseMixin.render_to_response()
appelle elle-mêmeget_template_names()
qui ne fait que consulter l’attributtemplate_name
de la vue, par défaut. Deux autres mixins (SingleObjectTemplateResponseMixin
etMultipleObjectTemplateResponseMixin
) surchargent cette méthode pour offrir des valeurs par défaut plus souples lorsqu’il s’agit de manipuler des objets réels.ContextMixin
- Chaque vue intégrée nécessitant des données de contexte, comme pour effectuer le rendu d’un gabarit (y compris
TemplateResponseMixin
ci-dessus), doit appelerget_context_data()
en transmettant sous forme de paramètres nommés les données qu’elle souhaite y placer.get_context_data()
renvoie un dictionnaire ; dansContextMixin
, elle renvoie simplement ses paramètres nommés, mais il est fréquent de surcharger cela pour ajouter davantage de contenu dans ce dictionnaire.
Construction des vues fondées sur les classes de Django¶
Voyons comment deux des vues fondées sur les classes de Django sont construites à partir de mixins fournissant des fonctionnalités isolées. Nous examinerons DetailView
qui produit une vue de détail d’un objet et ListView
qui produit une liste d’objets, généralement à partir d’un jeu de requête, avec pagination facultative. Ceci nous amènera à étudier quatre mixins qui, combinées entre elles, fournissent des fonctionnalités utiles lors du traitement d’un objet Django unique ou d’un ensemble d’objets.
Il existe également des classes mixins dans les vues génériques d’édition (FormView
et les vues spécifiques aux modèles CreateView
, UpdateView
et DeleteView
), ainsi que dans les vues génériques centrées sur les dates. Ces classes sont décrites dans la documentation de référence des mixins.
DetailView
: traitement d’un seul objet Django¶
Pour afficher le détail d’un objet, il y a fondamentalement deux choses à faire : récupérer l’objet, puis produire une réponse TemplateResponse
à l’aide du gabarit adéquat, en transmettant l’objet dans le contexte.
Pour obtenir l’objet, DetailView
se base sur SingleObjectMixin
qui fournit une méthode get_object()
qui va rechercher l’objet en se basant sur l’URL de la requête (elle cherche les paramètres nommés pk
et slug
tels que déclarés dans la configuration d’URL et sélectionne l’objet en utilisant soit l’attribut model
de la vue, soit l’attribut queryset
si celui-ci est présent). SingleObjectMixin
surcharge aussi get_context_data()
, qui est utilisée dans toutes les vues fondées sur les classes de Django pour fournir les données de contexte servant au rendu des gabarits.
Pour produire la réponse TemplateResponse
, DetailView
utilise SingleObjectTemplateResponseMixin
, qui étend TemplateResponseMixin
, surchargeant get_template_names()
comme expliqué plus haut. Il fournit en réalité un bon nombre d’options élaborées, mais le nom que la plupart des gens vont utiliser est <étiquette_application>/<nom_modèle>_detail.html
. La partie _detail
peut être modifiée en définissant un attribut template_name_suffix
différent dans une sous-classe (par exemple, les vues génériques d’édition emploient _form
pour les vues de création et de mise à jour et _confirm_delete
pour les vues de suppression).
ListView
: traitement de plusieurs objets Django¶
Les listes d’objets suivent grosso modo le même modèle : récupérer d’abord une liste d’objets (potentiellement paginés), typiquement un objet QuerySet
, puis produire une réponse TemplateResponse
avec le gabarit adéquat pour exploiter cette liste d’objets.
Pour obtenir les objets, ListView
utilise MultipleObjectMixin
qui offre à la fois get_queryset()
et paginate_queryset()
. Au contraire de SingleObjectMixin
, il n’est pas nécessaire d’identifier des éléments d’URL pour déterminer le jeu de requête à traiter ; le comportement par défaut est de simplement utiliser l’attribut queryset
ou model
de la classe de vue. Une raison fréquente de vouloir surcharger ici get_queryset()
est d’adapter la liste d’objets de manière dynamique, par exemple en fonction de l’utilisateur actuel ou pour exclure des articles dont la date est dans le futur dans un blog.
MultipleObjectMixin
surcharge aussi get_context_data()
pour inclure les variables de contexte appropriées à la pagination (indiquant des valeurs vides si la pagination est désactivée). Elle compte sur la présence de object_list
dans les paramètres nommés transmis, ce dont se charge ListView
.
Pour produire une réponse TemplateResponse
, ListView
utilise ensuite MultipleObjectTemplateResponseMixin
; comme pour SingleObjectTemplateResponseMixin
ci-dessus, get_template_names()
est surchargée pour fournir une série d'options
dont la plus utilisée est le nom de gabarit <étiquette_application>/<nom_modèle>_list.html
où la partie _list
provient aussi de l’attribut template_name_suffix
(les vues génériques centrées sur les dates utilisent des suffixes tels que _archive
, _archive_year
, etc. pour utiliser différents gabarits correspondant aux différentes vues de listes spécialisées pour les dates).
Utilisation des mixins de vues fondées sur les classes de Django¶
Maintenant que nous avons vu comment les classes génériques fondées sur les classes de Django utilisent les mixins à disposition, examinons d’autres façons de les combiner. Nous allons naturellement toujours les combiner avec d’autres vues intégrées ou génériques fondées sur les classes, mais les cas d’utilisation auxquels Django répond ne recouvrent pas toutes les situations plus rares auxquelles on peut être confronté.
Avertissement
On ne peut pas combiner tous les mixins, ni combiner n’importe quel mixin avec toutes les vues génériques fondées sur les classes. Nous présentons ici quelques exemples qui fonctionnent ; si vous souhaitez construire d’autres fonctionnalités, vous devrez étudier les interactions entre les attributs et les méthodes qui se chevauchent entre les différentes classes utilisées, ainsi que la façon dont l’ordre de résolution des méthodes affecte la version des méthodes appelée et l’ordre de ces appels.
La documentation de référence des vues fondées sur les classes et des mixins de vues fondées sur les classes de Django vous aidera à comprendre quels attributs et méthodes peuvent être la source de conflits entre les différentes classes et mixins.
Dans le doute, il est souvent préférable de simplifier les choses en se basant sur View
ou TemplateView
, peut-être avec SingleObjectMixin
ou MultipleObjectMixin
. Même si vous devez finalement écrire un peu plus de code, il sera probablement plus compréhensible pour quelqu’un devant s’y atteler plus tard, et en réduisant les interactions à surveiller, vous vous économiserez un peu de temps de réflexion (il est bien sûr toujours possible de se plonger dans l’implémentation de Django des vues génériques fondées sur les classes pour y chercher de l’inspiration sur la façon de traiter les problèmes).
Utilisation de SingleObjectMixin
avec View
¶
Si nous voulons écrire une classe de vue simple ne répondant qu’aux requêtes POST
, nous héritons de View
et écrivons une méthode post()
dans la sous-classe. Cependant, si le traitement doit porter sur un objet particulier identifié par l’URL, il est souhaitable de profiter de la fonctionnalité offerte par SingleObjectMixin
.
Nous allons démontrer cela avec le modèle Author
que nous avons utilisé dans l’introduction aux vues génériques basées sur les classes.
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.urls import reverse
from django.views import View
from django.views.generic.detail import SingleObjectMixin
from books.models import Author
class RecordInterest(SingleObjectMixin, View):
"""Records the current user's interest in an author."""
model = Author
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
# Look up the author we're interested in.
self.object = self.get_object()
# Actually record interest somehow here!
return HttpResponseRedirect(reverse('author-detail', kwargs={'pk': self.object.pk}))
En pratique, l’intérêt devrait probablement être enregistré dans un stockage clé-valeur plutôt que dans une base de données relationnelle, nous avons donc laissé cet aspect de côté. La seule partie de la vue qui peut profiter de l’utilisation de SingleObjectMixin
est l’endroit où il s’agit de récupérer l’auteur concerné par l’intérêt, ce qui est fait en appelant simplement self.get_object()
. Tout le reste est pris en charge pour nous par le mixin.
Nous pouvons assez facilement brancher cette vue dans nos URL :
from django.conf.urls import url
from books.views import RecordInterest
urlpatterns = [
#...
url(r'^author/(?P<pk>[0-9]+)/interest/$', RecordInterest.as_view(), name='author-interest'),
]
Remarquez le groupe nommé pk
qui sera utilisé par get_object()
pour rechercher l’instance Author
. Il est aussi possible d’utiliser un « slug » ou toute autre fonctionnalité de SingleObjectMixin
.
Utilisation de SingleObjectMixin
avec ListView
¶
ListView
intègre la pagination, mais il peut être par exemple souhatiable de paginer une liste d’objets qui sont tous liés (par clé étrangère) à un autre objet. Dans notre exemple de publication, on pourrait vouloir paginer tous les livres d’un éditeur particulier.
Une façon de faire cela serait de combiner ListView
avec SingleObjectMixin
, afin que le jeu de requête de la liste de livres paginée puisse dépendre de l’éditeur trouvé comme objet unique. Afin de faire cela, il est nécessaire d’avoir deux jeux de requête différents :
- Le jeu de requête
Book
utilisé parListView
- Comme nous avons accès à l’éditeur
Publisher
des livres que nous souhaitons afficher, nous surchargeons simplementget_queryset()
et utilisons le gestionnaire inverse de clé étrangère de l’objetPublisher
. - Le jeu de requête
Publisher
utilisé parget_object()
- Nous comptons sur l’implémentation par défaut de
get_object()
pour récupérer le bon objetPublisher
. Cependant, nous devons explicitement transmettre un paramètrequeryset
car sinon, l’implémentation par défaut deget_object()
appelleraitget_queryset()
que nous avons surchargée pour renvoyer des objetsBook
au lieu d’objetsPublisher
.
Note
Il faut être prudent avec get_context_data()
. Comme les deux classes SingleObjectMixin
et ListView
vont placer des éléments dans les données de contexte sous la valeur context_object_name
, si elle est définie, nous allons plutôt nous assurer de manière explicite que l’objet Publisher
se trouve bien dans les données de contexte. ListView
se chargera d’ajouter les contenus page_obj
et paginator
adéquats pour autant que nous n’oubliions pas d’appeler super()
.
Nous pouvons maintenant écrire une nouvelle vue PublisherDetail
:
from django.views.generic import ListView
from django.views.generic.detail import SingleObjectMixin
from books.models import Publisher
class PublisherDetail(SingleObjectMixin, ListView):
paginate_by = 2
template_name = "books/publisher_detail.html"
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=Publisher.objects.all())
return super(PublisherDetail, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(PublisherDetail, self).get_context_data(**kwargs)
context['publisher'] = self.object
return context
def get_queryset(self):
return self.object.book_set.all()
Remarquez notre manière de définir self.object
dans get()
afin que nous puissions ensuite l’utiliser dans get_context_data()
et get_queryset()
. Si vous ne définissez pas template_name
, le nom du gabarit prendra la valeur par défaut choisie par ListView
, ce qui donnera dans ce cas "books/book_list.html"
car il s’agit d’une liste de livres. ListView
n’a pas conscience de la présence de SingleObjectMixin
, elle ne sait donc pas du tout que cette vue a quelque chose à voir avec un objet Publisher
.
Nous avons délibérément défini paginate_by
à un petit nombre dans l’exemple afin que vous n’ayez pas à créer beaucoup de livres pour voir la pagination à l’œuvre ! Voici le gabarit que vous pourriez utiliser :
{% extends "base.html" %}
{% block content %}
<h2>Publisher {{ publisher.name }}</h2>
<ol>
{% for book in page_obj %}
<li>{{ book.title }}</li>
{% endfor %}
</ol>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
{% endif %}
</span>
</div>
{% endblock %}
Risque de trop grande complexité¶
Généralement, il est possible d’utiliser TemplateResponseMixin
et SingleObjectMixin
lorsque leurs fonctionnalités sont nécessaires. Comme montré plus haut, en prenant certaines précautions, on peut même combiner SingleObjectMixin
avec ListView
. Cependant, les choses deviennent de plus en plus complexes avec ces combinaisons ; voici une bonne règle générale :
Indice
Chacune de vos vues ne devrait utiliser que des mixins ou des vues en provenance de l’un des groupes de vues génériques fondées sur les classes : détail, liste, édition et date. Par exemple, il est convenable de combiner TemplateView
(vue intégrée) avec MultipleObjectMixin
(liste générique), mais vous rencontrerez sûrement des problèmes si vous combinez SingleObjectMixin
(détail générique) avec MultipleObjectMixin
(liste générique).
Pour montrer ce qui arrive quand on essaie de trop compliquer, nous présentons un exemple qui sacrifie la lisibilité et la maintenabilité alors qu’il existe une solution plus simple. Examinons d’abord une tentative naïve de combiner DetailView
avec FormMixin
pour permettre l’envoi POST
par un formulaire Form
Django vers la même URL qui est utilisée pour afficher un objet avec DetailView
.
Utilisation de FormMixin
avec DetailView
¶
Rappelez-vous notre exemple précédent qui combinait View
et SingleObjectMixin
. Nous enregistrions l’intérêt d’un utilisateur pour un auteur particulier. Admettons que nous voulions maintenant donner la possibilité d’écrire un message motivant leur intérêt. Nous partons encore une fois du principe que nous ne stockerons pas cela dans une base de données relationnelle, mais plutôt dans quelque chose de plus exotique dont nous ne nous soucierons pas dans cet exemple.
À ce stade, il est naturel de faire appel à un formulaire Form
pour englober les informations envoyées par le navigateur de l’utilisateur vers Django. En admettant que nous adhérons aussi aux principes de REST, nous aimerions utiliser la même URL pour l’affichage de l’auteur que pour la capture du message en provenance de l’utilisateur. Réécrivons notre vue AuthorDetailView
dans cette optique.
Nous conserverons le traitement GET
de la classe DetailView
, même s’il faudra rajouter un formulaire dans les données de contexte pour pouvoir l’afficher dans le gabarit. Nous allons aussi profiter du traitement de formulaire de FormMixin
et écrire un peu de code afin qu’en cas d’envoi POST
, le formulaire soit instancié de manière appropriée.
Note
Nous utilisons FormMixin
et implémentons nous-même la méthode post()
plutôt que d’essayer de mélanger DetailView
avec FormView
(qui fournit déjà une méthode post()
exploitable), parce que ces deux vues implémentent get()
et que cela risquerait d’ajouter de la confusion.
Voici à quoi ressemble notre nouvelle vue AuthorDetail
:
# CAUTION: you almost certainly do not want to do this.
# It is provided as part of a discussion of problems you can
# run into when combining different generic class-based view
# functionality that is not designed to be used together.
from django import forms
from django.http import HttpResponseForbidden
from django.urls import reverse
from django.views.generic import DetailView
from django.views.generic.edit import FormMixin
from books.models import Author
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDetail(FormMixin, DetailView):
model = Author
form_class = AuthorInterestForm
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(AuthorDetail, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
# Here, we would record the user's interest using the message
# passed in form.cleaned_data['message']
return super(AuthorDetail, self).form_valid(form)
get_success_url()
ne sert qu’à indiquer une destination de redirection, ce qui est utilisé dans l’implémentation par défaut de form_valid()
. Nous devons fournir notre propre méthode post()
, comme expliqué précédemment, et surcharger get_context_data()
pour que le formulaire Form
soit disponible dans les données de contexte.
Une meilleure solution¶
Il est assez évident que le nombre d’interactions subtiles entre FormMixin
et DetailView
éprouve déjà nos capacités conceptuelles. Il est improbable que vous ayez envie d’écrire vous-même ce genre de classe.
Dans ce cas, il serait relativement facile d’écrire simplement la méthode post()
vous-même, en conservant DetailView
comme seule fonctionnalité générique, même si l’écriture du code de gestion des formulaires implique beaucoup de duplication.
Sinon, une approche certainement plus simple que ci-dessus serait d’avoir une vue séparée pour le traitement du formulaire, ce qui permettrait d’utiliser sans problème FormView
séparément de DetailView
.
Une meilleure solution alternative¶
En réalité, ce que nous essayons de faire ici est d’utiliser deux vues classes différentes pour la même URL. Pourquoi donc ne pas simplement faire cela ? La division est ici très claire : les requêtes GET
devraient aboutir à la vue DetailView
(avec en plus le formulaire dans les données de contexte) et les requêtes POST
à la vue FormView
. Mettons d’abord en place ces deux vues.
La vue AuthorDisplay
est presque la même que celle que nous avons présentée en introduction. Nous devons écrire notre propre méthode get_context_data()
pour mettre à disposition AuthorInterestForm
dans le gabarit. Nous omettons la surcharge de get_object()
de la version précédente par souci de clarté :
from django.views.generic import DetailView
from django import forms
from books.models import Author
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDisplay(DetailView):
model = Author
def get_context_data(self, **kwargs):
context = super(AuthorDisplay, self).get_context_data(**kwargs)
context['form'] = AuthorInterestForm()
return context
Puis pour AuthorInterest
, il s’agit d’une simple sous-classe de FormView
, mais nous devons y adjoindre SingleObjectMixin
pour pouvoir obtenir l’auteur concerné par l’action en cours et ne pas oublier de définir template_name
pour s’assurer que les erreurs de formulaires soient affichées dans le même gabarit que AuthorDisplay
utilise lors de la requête GET
:
from django.urls import reverse
from django.http import HttpResponseForbidden
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
class AuthorInterest(SingleObjectMixin, FormView):
template_name = 'books/author_detail.html'
form_class = AuthorInterestForm
model = Author
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super(AuthorInterest, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
Pour terminer, nous lions le tout en créant une nouvelle vue AuthorDetail
. Nous savons déjà que l’appel à as_view()
sur une vue fondée sur les classes nous renvoie un objet se comportant exactement comme une vue de type fonction. Nous pouvons donc effectuer cet appel au moment où il faut choisir entre les deux « sous-vues ».
Vous pouvez bien sûr transmettre des paramètres nommés à as_view()
comme vous le feriez dans la configuration d’URL, par exemple si vous vouliez que le comportement de AuthorInterest
puisse aussi être utilisé avec une autre URL mais en affichant un gabarit différent :
from django.views import View
class AuthorDetail(View):
def get(self, request, *args, **kwargs):
view = AuthorDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = AuthorInterest.as_view()
return view(request, *args, **kwargs)
Cette approche peut également être employée avec toute autre vue fondée sur les classes, générique ou créée par vos soins, héritant directement de View
ou de TemplateView
, car elle conserve les différentes vues aussi distinctes que possible.
Plus que du simple code HTML¶
Les vues fondées sur les classes se révèlent particulièrement utiles au moment où l’on a besoin de répéter plusieurs fois un processus semblable. Imaginez qu’il faille écrire une API et que chaque vue doive renvoyer du JSON au lieu de produire une page HTML.
Il est alors possible de créer une classe mixin afin de l’utiliser dans chaque vue, gérant ainsi à un seul endroit la conversion vers le format JSON.
Par exemple, une simple classe mixin pour JSON pourrait ressembler à ceci :
from django.http import JsonResponse
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return JsonResponse(
self.get_data(context),
**response_kwargs
)
def get_data(self, context):
"""
Returns an object that will be serialized as JSON by json.dumps().
"""
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return context
Note
Consultez la documentation Sérialisation d’objets Django pour plus d’informations sur la façon de transformer correctement des modèles Django et des jeux de requête au format JSON.
Ce mixin fournit une méthode render_to_json_response()
ayant la même signature que render_to_response()
. Pour l’utiliser, nous devons simplement la combiner par exemple à une classe TemplateView
et surcharger render_to_response()
afin d’appeler render_to_json_response()
à la place :
from django.views.generic import TemplateView
class JSONView(JSONResponseMixin, TemplateView):
def render_to_response(self, context, **response_kwargs):
return self.render_to_json_response(context, **response_kwargs)
Il serait aussi possible d’utiliser cette classe mixin avec l’une des vues génériques. On peut composer sa propre version de DetailView
en combinant JSONResponseMixin
avec django.views.generic.detail.BaseDetailView
(celle-ci contenant le comportement de DetailView
avant le rendu du gabarit) :
from django.views.generic.detail import BaseDetailView
class JSONDetailView(JSONResponseMixin, BaseDetailView):
def render_to_response(self, context, **response_kwargs):
return self.render_to_json_response(context, **response_kwargs)
Cette vue peut ensuite être déployée de la même façon que pour n’importe quelle vue DetailView
, reproduisant le même comportement à l’exception du format de la réponse.
Si vous avez de réels goûts d’aventure, vous pourriez même tenter de combiner avec une sous-classe de DetailView
capable de renvoyer à la fois du contenu HTML et JSON en fonction d’une propriété de la requête HTTP, telle qu’un paramètre de requête ou un en-tête HTTP. Il suffit de combiner JSONResponseMixin
avec SingleObjectTemplateResponseMixin
et de surcharger l’implémentation de render_to_response()
pour déléguer le rendu à la méthode appropriée en fonction du type de réponse demandée par l’utilisateur :
from django.views.generic.detail import SingleObjectTemplateResponseMixin
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(HybridDetailView, self).render_to_response(context)
En raison de la façon dont Python résout la surcharge des méthodes, l’appel à super(HybridDetailView, self).render_to_response(context)
fait en réalité appel à l’implémentation render_to_response()
de TemplateResponseMixin
.