===================================Using mixins with class-based views===================================.. caution::This is an advanced topic. A working knowledge of :doc:`Django'sclass-based views<index>` is advised before exploring thesetechniques.Django's built-in class-based views provide a lot of functionality,but some of it you may want to use separately. For instance, you maywant to write a view that renders a template to make the HTTPresponse, but you can't use:class:`~django.views.generic.base.TemplateView`; perhaps you need torender a template only on ``POST``, with ``GET`` doing something elseentirely. While you could use:class:`~django.template.response.TemplateResponse` directly, thiswill likely result in duplicate code.For this reason, Django also provides a number of mixins that providemore discrete functionality. Template rendering, for instance, isencapsulated in the:class:`~django.views.generic.base.TemplateResponseMixin`. The Djangoreference documentation contains :doc:`full documentation of all themixins</ref/class-based-views/mixins>`.Context and template responses==============================Two central mixins are provided that help in providing a consistentinterface to working with templates in class-based views.:class:`~django.views.generic.base.TemplateResponseMixin`Every built in view which returns a:class:`~django.template.response.TemplateResponse` will call the:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`method that ``TemplateResponseMixin`` provides. Most of the time thiswill be called for you (for instance, it is called by the ``get()`` methodimplemented by both :class:`~django.views.generic.base.TemplateView` and:class:`~django.views.generic.detail.DetailView`); similarly, it's unlikelythat you'll need to override it, although if you want your response toreturn something not rendered via a Django template then you'll want to doit. For an example of this, see the :ref:`JSONResponseMixin example<jsonresponsemixin-example>`.``render_to_response()`` itself calls:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`,which by default will look up:attr:`~django.views.generic.base.TemplateResponseMixin.template_name` onthe class-based view; two other mixins(:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`and:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`)override this to provide more flexible defaults when dealing with actualobjects.:class:`~django.views.generic.base.ContextMixin`Every built in view which needs context data, such as for rendering atemplate (including ``TemplateResponseMixin`` above), should call:meth:`~django.views.generic.base.ContextMixin.get_context_data()` passingany data they want to ensure is in there as keyword arguments.``get_context_data()`` returns a dictionary; in ``ContextMixin`` itreturns its keyword arguments, but it is common to override this to addmore members to the dictionary. You can also use the:attr:`~django.views.generic.base.ContextMixin.extra_context` attribute.Building up Django's generic class-based views==============================================Let's look at how two of Django's generic class-based views are builtout of mixins providing discrete functionality. We'll consider:class:`~django.views.generic.detail.DetailView`, which renders a"detail" view of an object, and:class:`~django.views.generic.list.ListView`, which will render a listof objects, typically from a queryset, and optionally paginatethem. This will introduce us to four mixins which between them provideuseful functionality when working with either a single Django object,or multiple objects.There are also mixins involved in the generic edit views(:class:`~django.views.generic.edit.FormView`, and the model-specificviews :class:`~django.views.generic.edit.CreateView`,:class:`~django.views.generic.edit.UpdateView` and:class:`~django.views.generic.edit.DeleteView`), and in thedate-based generic views. These arecovered in the :doc:`mixin referencedocumentation</ref/class-based-views/mixins>`.``DetailView``: working with a single Django object---------------------------------------------------To show the detail of an object, we basically need to do two things:we need to look up the object and then we need to make a:class:`~django.template.response.TemplateResponse` with a suitable template,and that object as context.To get the object, :class:`~django.views.generic.detail.DetailView`relies on :class:`~django.views.generic.detail.SingleObjectMixin`,which provides a:meth:`~django.views.generic.detail.SingleObjectMixin.get_object`method that figures out the object based on the URL of the request (itlooks for ``pk`` and ``slug`` keyword arguments as declared in theURLConf, and looks the object up either from the:attr:`~django.views.generic.detail.SingleObjectMixin.model` attributeon the view, or the:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`attribute if that's provided). ``SingleObjectMixin`` also overrides:meth:`~django.views.generic.base.ContextMixin.get_context_data()`,which is used across all Django's built in class-based views to supplycontext data for template renders.To then make a :class:`~django.template.response.TemplateResponse`,:class:`DetailView` uses:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,which extends :class:`~django.views.generic.base.TemplateResponseMixin`,overriding:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`as discussed above. It actually provides a fairly sophisticated set of options,but the main one that most people are going to use is``<app_label>/<model_name>_detail.html``. The ``_detail`` part can be changedby setting:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`on a subclass to something else. (For instance, the :doc:`generic editviews<generic-editing>` use ``_form`` for create and update views, and``_confirm_delete`` for delete views.)``ListView``: working with many Django objects----------------------------------------------Lists of objects follow roughly the same pattern: we need a (possiblypaginated) list of objects, typically a:class:`~django.db.models.query.QuerySet`, and then we need to make a:class:`~django.template.response.TemplateResponse` with a suitable templateusing that list of objects.To get the objects, :class:`~django.views.generic.list.ListView` uses:class:`~django.views.generic.list.MultipleObjectMixin`, whichprovides both:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`and:meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`. Unlikewith :class:`~django.views.generic.detail.SingleObjectMixin`, there's no needto key off parts of the URL to figure out the queryset to work with, so thedefault uses the:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` or:attr:`~django.views.generic.list.MultipleObjectMixin.model` attributeon the view class. A common reason to override:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`here would be to dynamically vary the objects, such as depending onthe current user or to exclude posts in the future for a blog.:class:`~django.views.generic.list.MultipleObjectMixin` also overrides:meth:`~django.views.generic.base.ContextMixin.get_context_data()` toinclude appropriate context variables for pagination (providingdummies if pagination is disabled). It relies on ``object_list`` beingpassed in as a keyword argument, which :class:`ListView` arranges forit.To make a :class:`~django.template.response.TemplateResponse`,:class:`ListView` then uses:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`;as with :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`above, this overrides ``get_template_names()`` to provide :meth:`a range ofoptions <django.views.generic.list.MultipleObjectTemplateResponseMixin>`,with the most commonly-used being``<app_label>/<model_name>_list.html``, with the ``_list`` part againbeing taken from the:attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`attribute. (The date based generic views use suffixes such as ``_archive``,``_archive_year`` and so on to use different templates for the variousspecialized date-based list views.)Using Django's class-based view mixins======================================Now we've seen how Django's generic class-based views use the provided mixins,let's look at other ways we can combine them. We're still going to be combiningthem with either built-in class-based views, or other generic class-basedviews, but there are a range of rarer problems you can solve than are providedfor by Django out of the box... warning::Not all mixins can be used together, and not all generic classbased views can be used with all other mixins. Here we present afew examples that do work; if you want to bring together otherfunctionality then you'll have to consider interactions betweenattributes and methods that overlap between the different classesyou're using, and how `method resolution order`_ will affect whichversions of the methods will be called in what order.The reference documentation for Django's :doc:`class-basedviews</ref/class-based-views/index>` and :doc:`class-based viewmixins</ref/class-based-views/mixins>` will help you inunderstanding which attributes and methods are likely to causeconflict between different classes and mixins.If in doubt, it's often better to back off and base your work on:class:`View` or :class:`TemplateView`, perhaps with:class:`~django.views.generic.detail.SingleObjectMixin` and:class:`~django.views.generic.list.MultipleObjectMixin`. Although youwill probably end up writing more code, it is more likely to be clearlyunderstandable to someone else coming to it later, and with fewerinteractions to worry about you will save yourself some thinking. (Ofcourse, you can always dip into Django's implementation of the genericclass-based views for inspiration on how to tackle problems.).. _method resolution order: https://www.python.org/download/releases/2.3/mro/Using ``SingleObjectMixin`` with View-------------------------------------If we want to write a class-based view that responds only to ``POST``, we'llsubclass :class:`~django.views.generic.base.View` and write a ``post()`` methodin the subclass. However if we want our processing to work on a particularobject, identified from the URL, we'll want the functionality provided by:class:`~django.views.generic.detail.SingleObjectMixin`.We'll demonstrate this with the ``Author`` model we used in the:doc:`generic class-based views introduction<generic-display>`... code-block:: python:caption: ``views.py``from django.http import HttpResponseForbidden, HttpResponseRedirectfrom django.urls import reversefrom django.views import Viewfrom django.views.generic.detail import SingleObjectMixinfrom books.models import Authorclass RecordInterestView(SingleObjectMixin, View):"""Records the current user's interest in an author."""model = Authordef 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}))In practice you'd probably want to record the interest in a key-valuestore rather than in a relational database, so we've left that bitout. The only bit of the view that needs to worry about using:class:`~django.views.generic.detail.SingleObjectMixin` is where we want tolook up the author we're interested in, which it does with a call to``self.get_object()``. Everything else is taken care of for us by the mixin.We can hook this into our URLs easily enough:.. code-block:: python:caption: ``urls.py``from django.urls import pathfrom books.views import RecordInterestViewurlpatterns = [#...path('author/<int:pk>/interest/', RecordInterestView.as_view(), name='author-interest'),]Note the ``pk`` named group, which:meth:`~django.views.generic.detail.SingleObjectMixin.get_object` usesto look up the ``Author`` instance. You could also use a slug, orany of the other features of:class:`~django.views.generic.detail.SingleObjectMixin`.Using ``SingleObjectMixin`` with ``ListView``---------------------------------------------:class:`~django.views.generic.list.ListView` provides built-inpagination, but you might want to paginate a list of objects that areall linked (by a foreign key) to another object. In our publishingexample, you might want to paginate through all the books by aparticular publisher.One way to do this is to combine :class:`ListView` with:class:`~django.views.generic.detail.SingleObjectMixin`, so that the querysetfor the paginated list of books can hang off the publisher found as the singleobject. In order to do this, we need to have two different querysets:``Book`` queryset for use by :class:`~django.views.generic.list.ListView`Since we have access to the ``Publisher`` whose books we want to list, weoverride ``get_queryset()`` and use the ``Publisher``’s :ref:`reverseforeign key manager<backwards-related-objects>`.``Publisher`` queryset for use in :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`We'll rely on the default implementation of ``get_object()`` to fetch thecorrect ``Publisher`` object.However, we need to explicitly pass a ``queryset`` argument becauseotherwise the default implementation of ``get_object()`` would call``get_queryset()`` which we have overridden to return ``Book`` objectsinstead of ``Publisher`` ones... note::We have to think carefully about ``get_context_data()``.Since both :class:`~django.views.generic.detail.SingleObjectMixin` and:class:`ListView` willput things in the context data under the value of``context_object_name`` if it's set, we'll instead explicitlyensure the ``Publisher`` is in the context data. :class:`ListView`will add in the suitable ``page_obj`` and ``paginator`` for usproviding we remember to call ``super()``.Now we can write a new ``PublisherDetailView``::from django.views.generic import ListViewfrom django.views.generic.detail import SingleObjectMixinfrom books.models import Publisherclass PublisherDetailView(SingleObjectMixin, ListView):paginate_by = 2template_name = "books/publisher_detail.html"def get(self, request, *args, **kwargs):self.object = self.get_object(queryset=Publisher.objects.all())return super().get(request, *args, **kwargs)def get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)context['publisher'] = self.objectreturn contextdef get_queryset(self):return self.object.book_set.all()Notice how we set ``self.object`` within ``get()`` so wecan use it again later in ``get_context_data()`` and ``get_queryset()``.If you don't set ``template_name``, the template will default to the normal:class:`ListView` choice, which in this case would be``"books/book_list.html"`` because it's a list of books;:class:`ListView` knows nothing about:class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't haveany clue this view is anything to do with a ``Publisher``.The ``paginate_by`` is deliberately small in the example so you don'thave to create lots of books to see the pagination working! Here's thetemplate you'd want to use:.. code-block:: html+django{% 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 %}Avoid anything more complex===========================Generally you can use:class:`~django.views.generic.base.TemplateResponseMixin` and:class:`~django.views.generic.detail.SingleObjectMixin` when you needtheir functionality. As shown above, with a bit of care you can evencombine ``SingleObjectMixin`` with:class:`~django.views.generic.list.ListView`. However things getincreasingly complex as you try to do so, and a good rule of thumb is:.. hint::Each of your views should use only mixins or views from one of thegroups of generic class-based views: :doc:`detail,list<generic-display>`, :doc:`editing<generic-editing>` anddate. For example it's fine to combine:class:`TemplateView` (built in view) with:class:`~django.views.generic.list.MultipleObjectMixin` (generic list), butyou're likely to have problems combining ``SingleObjectMixin`` (genericdetail) with ``MultipleObjectMixin`` (generic list).To show what happens when you try to get more sophisticated, we showan example that sacrifices readability and maintainability when thereis a simpler solution. First, let's look at a naive attempt to combine:class:`~django.views.generic.detail.DetailView` with:class:`~django.views.generic.edit.FormMixin` to enable us to``POST`` a Django :class:`~django.forms.Form` to the same URL as we'redisplaying an object using :class:`DetailView`.Using ``FormMixin`` with ``DetailView``---------------------------------------Think back to our earlier example of using :class:`View` and:class:`~django.views.generic.detail.SingleObjectMixin` together. We wererecording a user's interest in a particular author; say now that we want tolet them leave a message saying why they like them. Again, let's assume we'renot going to store this in a relational database but instead insomething more esoteric that we won't worry about here.At this point it's natural to reach for a :class:`~django.forms.Form` toencapsulate the information sent from the user's browser to Django. Say alsothat we're heavily invested in `REST`_, so we want to use the same URL fordisplaying the author as for capturing the message from theuser. Let's rewrite our ``AuthorDetailView`` to do that... _REST: https://en.wikipedia.org/wiki/Representational_state_transferWe'll keep the ``GET`` handling from :class:`DetailView`, althoughwe'll have to add a :class:`~django.forms.Form` into the context data so we canrender it in the template. We'll also want to pull in form processingfrom :class:`~django.views.generic.edit.FormMixin`, and write a bit ofcode so that on ``POST`` the form gets called appropriately... note::We use :class:`~django.views.generic.edit.FormMixin` and implement``post()`` ourselves rather than try to mix :class:`DetailView` with:class:`FormView` (which provides a suitable ``post()`` already) becauseboth of the views implement ``get()``, and things would get much moreconfusing.Our new ``AuthorDetailView`` looks like this::# 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 formsfrom django.http import HttpResponseForbiddenfrom django.urls import reversefrom django.views.generic import DetailViewfrom django.views.generic.edit import FormMixinfrom books.models import Authorclass AuthorInterestForm(forms.Form):message = forms.CharField()class AuthorDetailView(FormMixin, DetailView):model = Authorform_class = AuthorInterestFormdef get_success_url(self):return reverse('author-detail', kwargs={'pk': self.object.pk})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().form_valid(form)``get_success_url()`` provides somewhere to redirect to, which gets usedin the default implementation of ``form_valid()``. We have to provide ourown ``post()`` as noted earlier.A better solution-----------------The number of subtle interactions between:class:`~django.views.generic.edit.FormMixin` and :class:`DetailView` isalready testing our ability to manage things. It's unlikely you'd want towrite this kind of class yourself.In this case, you could write the ``post()`` method yourself, keeping:class:`DetailView` as the only generic functionality, although writing:class:`~django.forms.Form` handling code involves a lot of duplication.Alternatively, it would still be less work than the above approach tohave a separate view for processing the form, which could use:class:`~django.views.generic.edit.FormView` distinct from:class:`DetailView` without concerns.An alternative better solution------------------------------What we're really trying to do here is to use two different classbased views from the same URL. So why not do just that? We have a veryclear division here: ``GET`` requests should get the:class:`DetailView` (with the :class:`~django.forms.Form` added to the contextdata), and ``POST`` requests should get the :class:`FormView`. Let'sset up those views first.The ``AuthorDetailView`` view is almost the same as :ref:`when wefirst introduced AuthorDetailView<generic-views-extra-work>`; we have towrite our own ``get_context_data()`` to make the``AuthorInterestForm`` available to the template. We'll skip the``get_object()`` override from before for clarity::from django import formsfrom django.views.generic import DetailViewfrom books.models import Authorclass AuthorInterestForm(forms.Form):message = forms.CharField()class AuthorDetailView(DetailView):model = Authordef get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)context['form'] = AuthorInterestForm()return contextThen the ``AuthorInterestForm`` is a :class:`FormView`, but we have to bring in:class:`~django.views.generic.detail.SingleObjectMixin` so we can find theauthor we're talking about, and we have to remember to set ``template_name`` toensure that form errors will render the same template as ``AuthorDetailView``is using on ``GET``::from django.http import HttpResponseForbiddenfrom django.urls import reversefrom django.views.generic import FormViewfrom django.views.generic.detail import SingleObjectMixinclass AuthorInterestFormView(SingleObjectMixin, FormView):template_name = 'books/author_detail.html'form_class = AuthorInterestFormmodel = Authordef post(self, request, *args, **kwargs):if not request.user.is_authenticated:return HttpResponseForbidden()self.object = self.get_object()return super().post(request, *args, **kwargs)def get_success_url(self):return reverse('author-detail', kwargs={'pk': self.object.pk})Finally we bring this together in a new ``AuthorView`` view. Wealready know that calling :meth:`~django.views.generic.base.View.as_view()` ona class-based view gives us something that behaves exactly like a functionbased view, so we can do that at the point we choose between the two subviews.You can pass through keyword arguments to:meth:`~django.views.generic.base.View.as_view()` in the same way youwould in your URLconf, such as if you wanted the ``AuthorInterestFormView``behavior to also appear at another URL but using a different template::from django.views import Viewclass AuthorView(View):def get(self, request, *args, **kwargs):view = AuthorDetailView.as_view()return view(request, *args, **kwargs)def post(self, request, *args, **kwargs):view = AuthorInterestFormView.as_view()return view(request, *args, **kwargs)This approach can also be used with any other generic class-basedviews or your own class-based views inheriting directly from:class:`View` or :class:`TemplateView`, as it keeps the differentviews as separate as possible... _jsonresponsemixin-example:More than just HTML===================Where class-based views shine is when you want to do the same thing many times.Suppose you're writing an API, and every view should return JSON instead ofrendered HTML.We can create a mixin class to use in all of our views, handling theconversion to JSON once.For example, a JSON mixin might look something like this::from django.http import JsonResponseclass JSONResponseMixin:"""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::Check out the :doc:`/topics/serialization` documentation for moreinformation on how to correctly transform Django models and querysets intoJSON.This mixin provides a ``render_to_json_response()`` method with the same signatureas :func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`.To use it, we need to mix it into a ``TemplateView`` for example, and override``render_to_response()`` to call ``render_to_json_response()`` instead::from django.views.generic import TemplateViewclass JSONView(JSONResponseMixin, TemplateView):def render_to_response(self, context, **response_kwargs):return self.render_to_json_response(context, **response_kwargs)Equally we could use our mixin with one of the generic views. We can make ourown version of :class:`~django.views.generic.detail.DetailView` by mixing``JSONResponseMixin`` with the:class:`~django.views.generic.detail.BaseDetailView` -- (the:class:`~django.views.generic.detail.DetailView` before templaterendering behavior has been mixed in)::from django.views.generic.detail import BaseDetailViewclass JSONDetailView(JSONResponseMixin, BaseDetailView):def render_to_response(self, context, **response_kwargs):return self.render_to_json_response(context, **response_kwargs)This view can then be deployed in the same way as any other:class:`~django.views.generic.detail.DetailView`, with exactly thesame behavior -- except for the format of the response.If you want to be really adventurous, you could even mix a:class:`~django.views.generic.detail.DetailView` subclass that is ableto return *both* HTML and JSON content, depending on some property ofthe HTTP request, such as a query argument or an HTTP header. Mix in both the``JSONResponseMixin`` and a:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,and override the implementation of:func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`to defer to the appropriate rendering method depending on the type of responsethat the user requested::from django.views.generic.detail import SingleObjectTemplateResponseMixinclass HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):def render_to_response(self, context):# Look for a 'format=json' GET argumentif self.request.GET.get('format') == 'json':return self.render_to_json_response(context)else:return super().render_to_response(context)Because of the way that Python resolves method overloading, the call to``super().render_to_response(context)`` ends up calling the:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`implementation of :class:`~django.views.generic.base.TemplateResponseMixin`.