summaryrefslogtreecommitdiff
path: root/docs/topics/class-based-views
diff options
context:
space:
mode:
authorTobias Kunze <r@rixx.de>2019-06-17 16:54:55 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-09-06 13:27:46 +0200
commit4a954cfd11a5d034491f87fcbc920eb97a302bb3 (patch)
tree1c92caae5d8a9b33c51ddd74b4b2061248f3915f /docs/topics/class-based-views
parentaddabc492bdc0191ac95d59ec34b56b34086ebb9 (diff)
downloaddjango-4a954cfd11a5d034491f87fcbc920eb97a302bb3.tar.gz
Fixed #30573 -- Rephrased documentation to avoid words that minimise the involved difficulty.
This patch does not remove all occurrences of the words in question. Rather, I went through all of the occurrences of the words listed below, and judged if they a) suggested the reader had some kind of knowledge/experience, and b) if they added anything of value (including tone of voice, etc). I left most of the words alone. I looked at the following words: - simply/simple - easy/easier/easiest - obvious - just - merely - straightforward - ridiculous Thanks to Carlton Gibson for guidance on how to approach this issue, and to Tim Bell for providing the idea. But the enormous lion's share of thanks go to Adam Johnson for his patient and helpful review.
Diffstat (limited to 'docs/topics/class-based-views')
-rw-r--r--docs/topics/class-based-views/generic-display.txt69
-rw-r--r--docs/topics/class-based-views/generic-editing.txt10
-rw-r--r--docs/topics/class-based-views/index.txt50
-rw-r--r--docs/topics/class-based-views/intro.txt42
-rw-r--r--docs/topics/class-based-views/mixins.txt59
5 files changed, 112 insertions, 118 deletions
diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt
index 8e39ad6c14..ad5bb31b8e 100644
--- a/docs/topics/class-based-views/generic-display.txt
+++ b/docs/topics/class-based-views/generic-display.txt
@@ -29,8 +29,8 @@ Django ships with generic views to do the following:
* Allow users to create, update, and delete objects -- with or
without authorization.
-Taken together, these views provide easy interfaces to perform the most common
-tasks developers encounter.
+Taken together, these views provide interfaces to perform the most common tasks
+developers encounter.
Extending generic views
@@ -43,10 +43,10 @@ Django developers is how to make generic views handle a wider array of
situations.
This is one of the reasons generic views were redesigned for the 1.3 release -
-previously, they were just view functions with a bewildering array of options;
-now, rather than passing in a large amount of configuration in the URLconf,
-the recommended way to extend generic views is to subclass them, and override
-their attributes or methods.
+previously, they were view functions with a bewildering array of options; now,
+rather than passing in a large amount of configuration in the URLconf, the
+recommended way to extend generic views is to subclass them, and override their
+attributes or methods.
That said, generic views will have a limit. If you find you're struggling to
implement your view as a subclass of a generic view, then you may find it more
@@ -63,8 +63,7 @@ Generic views of objects
:class:`~django.views.generic.base.TemplateView` certainly is useful, but
Django's generic views really shine when it comes to presenting views of your
database content. Because it's such a common task, Django comes with a handful
-of built-in generic views that make generating list and detail views of objects
-incredibly easy.
+of built-in generic views to help generate list and detail views of objects.
Let's start by looking at some examples of showing a list of objects or an
individual object.
@@ -130,7 +129,7 @@ however. We could explicitly tell the view which template to use by adding a
template Django will infer one from the object's name. In this case, the
inferred template will be ``"books/publisher_list.html"`` -- the "books" part
comes from the name of the app that defines the model, while the "publisher"
-bit is just the lowercased version of the model's name.
+bit is the lowercased version of the model's name.
.. note::
@@ -139,8 +138,8 @@ bit is just the lowercased version of the model's name.
be: /path/to/project/books/templates/books/publisher_list.html
This template will be rendered against a context containing a variable called
-``object_list`` that contains all the publisher objects. A very simple template
-might look like the following:
+``object_list`` that contains all the publisher objects. A template might look
+like the this:
.. code-block:: html+django
@@ -197,17 +196,16 @@ coworkers who design templates will thank you.
Adding extra context
--------------------
-Often you simply need to present some extra information beyond that
-provided by the generic view. For example, think of showing a list of
-all the books on each publisher detail page. The
-:class:`~django.views.generic.detail.DetailView` generic view provides
-the publisher to the context, but how do we get additional information
-in that template?
+Often you need to present some extra information beyond that provided by the
+generic view. For example, think of showing a list of all the books on each
+publisher detail page. The :class:`~django.views.generic.detail.DetailView`
+generic view provides the publisher to the context, but how do we get
+additional information in that template?
The answer is to subclass :class:`~django.views.generic.detail.DetailView`
and provide your own implementation of the ``get_context_data`` method.
-The default implementation simply adds the object being displayed to the
-template, but you can override it to send more::
+The default implementation adds the object being displayed to the template, but
+you can override it to send more::
from django.views.generic import DetailView
from books.models import Book, Publisher
@@ -261,16 +259,16 @@ specify the list of objects using the ``queryset`` argument::
context_object_name = 'publisher'
queryset = Publisher.objects.all()
-Specifying ``model = Publisher`` is really just shorthand for saying
-``queryset = Publisher.objects.all()``. However, by using ``queryset``
-to define a filtered list of objects you can be more specific about the
-objects that will be visible in the view (see :doc:`/topics/db/queries`
-for more information about :class:`~django.db.models.query.QuerySet` objects,
-and see the :doc:`class-based views reference </ref/class-based-views/index>`
-for the complete details).
+Specifying ``model = Publisher`` is shorthand for saying ``queryset =
+Publisher.objects.all()``. However, by using ``queryset`` to define a filtered
+list of objects you can be more specific about the objects that will be visible
+in the view (see :doc:`/topics/db/queries` for more information about
+:class:`~django.db.models.query.QuerySet` objects, and see the
+:doc:`class-based views reference </ref/class-based-views/index>` for the
+complete details).
-To pick a simple example, we might want to order a list of books by
-publication date, with the most recent first::
+To pick an example, we might want to order a list of books by publication date,
+with the most recent first::
from django.views.generic import ListView
from books.models import Book
@@ -279,7 +277,7 @@ publication date, with the most recent first::
queryset = Book.objects.order_by('-publication_date')
context_object_name = 'book_list'
-That's a pretty simple example, but it illustrates the idea nicely. Of course,
+That's a pretty minimal example, but it illustrates the idea nicely. Of course,
you'll usually want to do more than just reorder objects. If you want to
present a list of books by a particular publisher, you can use the same
technique::
@@ -321,8 +319,8 @@ publisher?
Handily, the ``ListView`` has a
:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset` method we
-can override. Previously, it has just been returning the value of the
-``queryset`` attribute, but now we can add more logic.
+can override. By default, it returns the value of the ``queryset`` attribute,
+but we can use it to add more logic.
The key part to making this work is that when class-based views are called,
various useful things are stored on ``self``; as well as the request
@@ -354,9 +352,10 @@ Next, we'll write the ``PublisherBookList`` view itself::
self.publisher = get_object_or_404(Publisher, name=self.kwargs['publisher'])
return Book.objects.filter(publisher=self.publisher)
-As you can see, it's quite easy to add more logic to the queryset selection;
-if we wanted, we could use ``self.request.user`` to filter using the current
-user, or other more complex logic.
+Using ``get_queryset`` to add logic to the queryset selection is as convenient
+as it is powerful. For instance, if we wanted, we could use
+``self.request.user`` to filter using the current user, or other more complex
+logic.
We can also add the publisher into the context at the same time, so we can
use it in the template::
@@ -407,7 +406,7 @@ custom view::
]
Then we'd write our new view -- ``get_object`` is the method that retrieves the
-object -- so we simply override it and wrap the call::
+object -- so we override it and wrap the call::
from django.utils import timezone
from django.views.generic import DetailView
diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt
index 7124b146ac..c1e73fc2fb 100644
--- a/docs/topics/class-based-views/generic-editing.txt
+++ b/docs/topics/class-based-views/generic-editing.txt
@@ -16,7 +16,7 @@ processing.
Basic forms
===========
-Given a simple contact form:
+Given a contact form:
.. code-block:: python
:caption: forms.py
@@ -85,7 +85,7 @@ You don't even need to provide a ``success_url`` for
:meth:`~django.db.models.Model.get_absolute_url()` on the model object if available.
If you want to use a custom :class:`~django.forms.ModelForm` (for instance to
-add extra validation) simply set
+add extra validation), set
:attr:`~django.views.generic.edit.FormMixin.form_class` on your view.
.. note::
@@ -132,8 +132,8 @@ here; we don't have to write any logic ourselves:
success_url = reverse_lazy('author-list')
.. note::
- We have to use :func:`~django.urls.reverse_lazy` here, not just
- ``reverse()`` as the urls are not loaded when the file is imported.
+ We have to use :func:`~django.urls.reverse_lazy` instead of
+ ``reverse()``, as the urls are not loaded when the file is imported.
The ``fields`` attribute works the same way as the ``fields`` attribute on the
inner ``Meta`` class on :class:`~django.forms.ModelForm`. Unless you define the
@@ -225,7 +225,7 @@ handle unauthorized users in :meth:`~.ModelFormMixin.form_valid()`.
AJAX example
============
-Here is a simple example showing how you might go about implementing a form that
+Here is an example showing how you might go about implementing a form that
works for AJAX requests as well as 'normal' form POSTs::
from django.http import JsonResponse
diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt
index 17e8b66f8f..364746a093 100644
--- a/docs/topics/class-based-views/index.txt
+++ b/docs/topics/class-based-views/index.txt
@@ -6,10 +6,10 @@ A view is a callable which takes a request and returns a
response. This can be more than just a function, and Django provides
an example of some classes which can be used as views. These allow you
to structure your views and reuse code by harnessing inheritance and
-mixins. There are also some generic views for simple tasks which we'll
-get to later, but you may want to design your own structure of
-reusable views which suits your use case. For full details, see the
-:doc:`class-based views reference documentation</ref/class-based-views/index>`.
+mixins. There are also some generic views for tasks which we'll get to later,
+but you may want to design your own structure of reusable views which suits
+your use case. For full details, see the :doc:`class-based views reference
+documentation</ref/class-based-views/index>`.
.. toctree::
:maxdepth: 1
@@ -25,18 +25,18 @@ Basic examples
Django provides base view classes which will suit a wide range of applications.
All views inherit from the :class:`~django.views.generic.base.View` class, which
handles linking the view in to the URLs, HTTP method dispatching and other
-simple features. :class:`~django.views.generic.base.RedirectView` is for a
-simple HTTP redirect, and :class:`~django.views.generic.base.TemplateView`
-extends the base class to make it also render a template.
+common features. :class:`~django.views.generic.base.RedirectView` provides a
+HTTP redirect, and :class:`~django.views.generic.base.TemplateView` extends the
+base class to make it also render a template.
-Simple usage in your URLconf
-============================
+Usage in your URLconf
+=====================
-The simplest way to use generic views is to create them directly in your
-URLconf. If you're only changing a few simple attributes on a class-based view,
-you can simply pass them into the
-:meth:`~django.views.generic.base.View.as_view` method call itself::
+The most direct way to use generic views is to create them directly in your
+URLconf. If you're only changing a few attributes on a class-based view, you
+can pass them into the :meth:`~django.views.generic.base.View.as_view` method
+call itself::
from django.urls import path
from django.views.generic import TemplateView
@@ -59,8 +59,8 @@ existing view and override attributes (such as the ``template_name``) or
methods (such as ``get_context_data``) in your subclass to provide new values
or methods. Consider, for example, a view that just displays one template,
``about.html``. Django has a generic view to do this -
-:class:`~django.views.generic.base.TemplateView` - so we can just subclass it,
-and override the template name::
+:class:`~django.views.generic.base.TemplateView` - so we can subclass it, and
+override the template name::
# some_app/views.py
from django.views.generic import TemplateView
@@ -68,11 +68,10 @@ and override the template name::
class AboutView(TemplateView):
template_name = "about.html"
-Then we just need to add this new view into our URLconf.
-:class:`~django.views.generic.base.TemplateView` is a class, not a function,
-so we point the URL to the :meth:`~django.views.generic.base.View.as_view`
-class method instead, which provides a function-like entry to class-based
-views::
+Then we need to add this new view into our URLconf.
+:class:`~django.views.generic.base.TemplateView` is a class, not a function, so
+we point the URL to the :meth:`~django.views.generic.base.View.as_view` class
+method instead, which provides a function-like entry to class-based views::
# urls.py
from django.urls import path
@@ -123,9 +122,8 @@ And the view::
response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response
-If the view is accessed from a ``GET`` request, a plain-and-simple object
-list is returned in the response (using ``book_list.html`` template). But if
-the client issues a ``HEAD`` request, the response has an empty body and
-the ``Last-Modified`` header indicates when the most recent book was published.
-Based on this information, the client may or may not download the full object
-list.
+If the view is accessed from a ``GET`` request, an object list is returned in
+the response (using the ``book_list.html`` template). But if the client issues
+a ``HEAD`` request, the response has an empty body and the ``Last-Modified``
+header indicates when the most recent book was published. Based on this
+information, the client may or may not download the full object list.
diff --git a/docs/topics/class-based-views/intro.txt b/docs/topics/class-based-views/intro.txt
index b1212af585..6b31180cf9 100644
--- a/docs/topics/class-based-views/intro.txt
+++ b/docs/topics/class-based-views/intro.txt
@@ -25,7 +25,7 @@ these patterns and ease view development for the common cases.
The problem with function-based generic views is that while they covered the
simple cases well, there was no way to extend or customize them beyond some
-simple configuration options, limiting their usefulness in many real-world
+configuration options, limiting their usefulness in many real-world
applications.
Class-based generic views were created with the same objective as
@@ -35,9 +35,9 @@ results in class-based generic views being more extensible and flexible than
their function-based counterparts.
If you have tried function based generic views in the past and found them
-lacking, you should not think of class-based generic views as simply a
-class-based equivalent, but rather as a fresh approach to solving the original
-problems that generic views were meant to solve.
+lacking, you should not think of class-based generic views as a class-based
+equivalent, but rather as a fresh approach to solving the original problems
+that generic views were meant to solve.
The toolkit of base classes and mixins that Django uses to build class-based
generic views are built for maximum flexibility, and as such have many hooks in
@@ -45,11 +45,11 @@ the form of default method implementations and attributes that you are unlikely
to be concerned with in the simplest use cases. For example, instead of
limiting you to a class-based attribute for ``form_class``, the implementation
uses a ``get_form`` method, which calls a ``get_form_class`` method, which in
-its default implementation just returns the ``form_class`` attribute of the
-class. This gives you several options for specifying what form to use, from a
-simple attribute, to a fully dynamic, callable hook. These options seem to add
-hollow complexity for simple situations, but without them, more advanced
-designs would be limited.
+its default implementation returns the ``form_class`` attribute of the class.
+This gives you several options for specifying what form to use, from an
+attribute, to a fully dynamic, callable hook. These options seem to add hollow
+complexity for simple situations, but without them, more advanced designs would
+be limited.
Using class-based views
=======================
@@ -221,7 +221,7 @@ A similar class-based view might look like::
return render(request, self.template_name, {'form': form})
-This is a very simple case, but you can see that you would then have the option
+This is a minimal case, but you can see that you would then have the option
of customizing this view by overriding any of the class attributes, e.g.
``form_class``, via URLconf configuration, or subclassing and overriding one or
more of the methods (or both!).
@@ -236,9 +236,9 @@ differently depending on if you're using ``as_view()`` or creating a subclass.
Decorating in URLconf
---------------------
-The simplest way of decorating class-based views is to decorate the
-result of the :meth:`~django.views.generic.base.View.as_view` method.
-The easiest place to do this is in the URLconf where you deploy your view::
+You can adjust class-based views by decorating the result of the
+:meth:`~django.views.generic.base.View.as_view` method. The easiest place to do
+this is in the URLconf where you deploy your view::
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView
@@ -263,11 +263,11 @@ To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
:meth:`~django.views.generic.base.View.dispatch` method of the class.
-A method on a class isn't quite the same as a standalone function, so
-you can't just apply a function decorator to the method -- you need to
-transform it into a method decorator first. The ``method_decorator``
-decorator transforms a function decorator into a method decorator so
-that it can be used on an instance method. For example::
+A method on a class isn't quite the same as a standalone function, so you can't
+just apply a function decorator to the method -- you need to transform it into
+a method decorator first. The ``method_decorator`` decorator transforms a
+function decorator into a method decorator so that it can be used on an
+instance method. For example::
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
@@ -306,9 +306,9 @@ The decorators will process a request in the order they are passed to the
decorator. In the example, ``never_cache()`` will process the request before
``login_required()``.
-In this example, every instance of ``ProtectedView`` will have login protection.
-These examples use ``login_required``, however, the same behavior can be
-obtained more simply using
+In this example, every instance of ``ProtectedView`` will have login
+protection. These examples use ``login_required``, however, the same behavior
+can be obtained by using
:class:`~django.contrib.auth.mixins.LoginRequiredMixin`.
.. note::
diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt
index ad9fb7547b..fdc53e5c3b 100644
--- a/docs/topics/class-based-views/mixins.txt
+++ b/docs/topics/class-based-views/mixins.txt
@@ -46,7 +46,7 @@ interface to working with templates in class-based views.
``render_to_response()`` itself calls
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`,
- which by default will just look up
+ which by default will look up
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name` on
the class-based view; two other mixins
(:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
@@ -61,8 +61,8 @@ interface to working with templates in class-based views.
:meth:`~django.views.generic.base.ContextMixin.get_context_data()` passing
any data they want to ensure is in there as keyword arguments.
``get_context_data()`` returns a dictionary; in ``ContextMixin`` it
- simply returns its keyword arguments, but it is common to override this to
- add more members to the dictionary. You can also use the
+ returns its keyword arguments, but it is common to override this to add
+ more 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
@@ -142,7 +142,7 @@ and
:meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`. Unlike
with :class:`~django.views.generic.detail.SingleObjectMixin`, there's no need
to key off parts of the URL to figure out the queryset to work with, so the
-default just uses the
+default uses the
:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` or
:attr:`~django.views.generic.list.MultipleObjectMixin.model` attribute
on the view class. A common reason to override
@@ -212,11 +212,10 @@ the box.
Using ``SingleObjectMixin`` with View
-------------------------------------
-If we want to write a simple class-based view that responds only to
-``POST``, we'll subclass :class:`~django.views.generic.base.View` and
-write a ``post()`` method in the subclass. However if we want our
-processing to work on a particular object, identified from the URL,
-we'll want the functionality provided by
+If we want to write a class-based view that responds only to ``POST``, we'll
+subclass :class:`~django.views.generic.base.View` and write a ``post()`` method
+in the subclass. However if we want our processing to work on a particular
+object, 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
@@ -249,9 +248,8 @@ In practice you'd probably want to record the interest in a key-value
store rather than in a relational database, so we've left that bit
out. The only bit of the view that needs to worry about using
:class:`~django.views.generic.detail.SingleObjectMixin` is where we want to
-look up the author we're interested in, which it just does with a simple call
-to ``self.get_object()``. Everything else is taken care of for us by the
-mixin.
+look 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:
@@ -288,8 +286,8 @@ object. 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, we
- simply override ``get_queryset()`` and use the ``Publisher``’s
- :ref:`reverse foreign key manager<backwards-related-objects>`.
+ override ``get_queryset()`` and use the ``Publisher``’s :ref:`reverse
+ foreign 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 the
@@ -476,24 +474,23 @@ Our new ``AuthorDetail`` looks like this::
# passed in form.cleaned_data['message']
return super().form_valid(form)
-``get_success_url()`` is just providing somewhere to redirect to,
+``get_success_url()`` is provides somewhere to redirect to,
which gets used in the default implementation of
``form_valid()``. We have to provide our own ``post()`` as noted earlier.
A better solution
-----------------
-It should be obvious that the number of subtle interactions between
+The number of subtle interactions between
:class:`~django.views.generic.edit.FormMixin` and :class:`DetailView` is
already testing our ability to manage things. It's unlikely you'd want to
write this kind of class yourself.
-In this case, it would be fairly easy to just 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.
+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 easier than the above approach to
+Alternatively, it would still be less work than the above approach to
have a separate view for processing the form, which could use
:class:`~django.views.generic.edit.FormView` distinct from
:class:`DetailView` without concerns.
@@ -529,11 +526,11 @@ write our own ``get_context_data()`` to make the
context['form'] = AuthorInterestForm()
return context
-Then the ``AuthorInterest`` is a simple :class:`FormView`, but we
-have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we
-can find the author we're talking about, and we have to remember to set
-``template_name`` to ensure that form errors will render the same
-template as ``AuthorDisplay`` is using on ``GET``::
+Then the ``AuthorInterest`` is a :class:`FormView`, but we have to bring in
+:class:`~django.views.generic.detail.SingleObjectMixin` so we can find the
+author we're talking about, and we have to remember to set ``template_name`` to
+ensure that form errors will render the same template as ``AuthorDisplay`` is
+using on ``GET``::
from django.http import HttpResponseForbidden
from django.urls import reverse
@@ -593,7 +590,7 @@ rendered HTML.
We can create a mixin class to use in all of our views, handling the
conversion to JSON once.
-For example, a simple JSON mixin might look something like this::
+For example, a JSON mixin might look something like this::
from django.http import JsonResponse
@@ -628,8 +625,8 @@ For example, a simple JSON mixin might look something like this::
This mixin provides a ``render_to_json_response()`` method with the same signature
as :func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`.
-To use it, we simply need to mix it into a ``TemplateView`` for example,
-and override ``render_to_response()`` to call ``render_to_json_response()`` instead::
+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 TemplateView
@@ -657,8 +654,8 @@ same 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 able
to return *both* HTML and JSON content, depending on some property of
-the HTTP request, such as a query argument or a HTTP header. Just mix
-in both the ``JSONResponseMixin`` and a
+the HTTP request, such as a query argument or a 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()`