234 lines
8.6 KiB
Plaintext
234 lines
8.6 KiB
Plaintext
=================
|
|
Class-based views
|
|
=================
|
|
|
|
.. versionadded:: 1.3
|
|
|
|
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>`.
|
|
|
|
.. toctree::
|
|
:maxdepth: 1
|
|
|
|
generic-display
|
|
generic-editing
|
|
mixins
|
|
|
|
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.
|
|
|
|
|
|
Simple usage
|
|
============
|
|
|
|
Class-based generic views (and any class-based views that inherit from
|
|
the base classes Django provides) can be configured in two
|
|
ways: subclassing, or passing in arguments directly in the URLconf.
|
|
|
|
When you subclass a class-based view, you can 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::
|
|
|
|
# some_app/views.py
|
|
from django.views.generic import TemplateView
|
|
|
|
class AboutView(TemplateView):
|
|
template_name = "about.html"
|
|
|
|
Then, we just need to add this new view into our URLconf. As the class-based
|
|
views themselves are classes, we point the URL to the ``as_view`` class method
|
|
instead, which is the entry point for class-based views::
|
|
|
|
# urls.py
|
|
from django.conf.urls import patterns, url, include
|
|
from some_app.views import AboutView
|
|
|
|
urlpatterns = patterns('',
|
|
(r'^about/', AboutView.as_view()),
|
|
)
|
|
|
|
Alternatively, if you're only changing a few simple attributes on a
|
|
class-based view, you can simply pass the new attributes into the ``as_view``
|
|
method call itself::
|
|
|
|
from django.conf.urls import patterns, url, include
|
|
from django.views.generic import TemplateView
|
|
|
|
urlpatterns = patterns('',
|
|
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
|
)
|
|
|
|
A similar overriding pattern can be used for the ``url`` attribute on
|
|
:class:`~django.views.generic.base.RedirectView`.
|
|
|
|
.. _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 of
|
|
rendered HTML.
|
|
|
|
We can use 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::
|
|
|
|
import json
|
|
from django.http import HttpResponse
|
|
|
|
class JSONResponseMixin(object):
|
|
"""
|
|
A mixin that can be used to render a JSON response.
|
|
"""
|
|
response_class = HttpResponse
|
|
|
|
def render_to_response(self, context, **response_kwargs):
|
|
"""
|
|
Returns a JSON response, transforming 'context' to make the payload.
|
|
"""
|
|
response_kwargs['content_type'] = 'application/json'
|
|
return self.response_class(
|
|
self.convert_context_to_json(context),
|
|
**response_kwargs
|
|
)
|
|
|
|
def convert_context_to_json(self, context):
|
|
"Convert the context dictionary into a JSON object"
|
|
# 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 json.dumps(context)
|
|
|
|
Now we mix this into the base view::
|
|
|
|
from django.views.generic import View
|
|
|
|
class JSONView(JSONResponseMixin, View):
|
|
pass
|
|
|
|
Equally we could use our mixin with one of the generic views. We can make our
|
|
own version of :class:`~django.views.generic.detail.DetailView` by mixing
|
|
:class:`JSONResponseMixin` with the
|
|
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
|
:class:`~django.views.generic.detail.DetailView` before template
|
|
rendering behavior has been mixed in)::
|
|
|
|
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
|
pass
|
|
|
|
This view can then be deployed in the same way as any other
|
|
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
|
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 :class:`JSONResponseMixin` and a
|
|
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
|
and override the implementation of :func:`render_to_response()` to defer
|
|
to the appropriate subclass depending on the type of response that the user
|
|
requested::
|
|
|
|
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
|
def render_to_response(self, context):
|
|
# Look for a 'format=json' GET argument
|
|
if self.request.GET.get('format','html') == 'json':
|
|
return JSONResponseMixin.render_to_response(self, context)
|
|
else:
|
|
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
|
|
|
Because of the way that Python resolves method overloading, the local
|
|
``render_to_response()`` implementation will override the versions provided by
|
|
:class:`JSONResponseMixin` and
|
|
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
|
|
|
For more information on how to use the built in generic views, consult the next
|
|
topic on :doc:`generic class based views</topics/class-based-views/generic-display>`.
|
|
|
|
Decorating class-based views
|
|
============================
|
|
|
|
.. highlightlang:: python
|
|
|
|
The extension of class-based views isn't limited to using mixins. You
|
|
can use also use decorators.
|
|
|
|
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::
|
|
|
|
from django.contrib.auth.decorators import login_required, permission_required
|
|
from django.views.generic import TemplateView
|
|
|
|
from .views import VoteView
|
|
|
|
urlpatterns = patterns('',
|
|
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
|
|
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
|
|
)
|
|
|
|
This approach applies the decorator on a per-instance basis. If you
|
|
want every instance of a view to be decorated, you need to take a
|
|
different approach.
|
|
|
|
.. _decorating-class-based-views:
|
|
|
|
Decorating the class
|
|
--------------------
|
|
|
|
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::
|
|
|
|
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(ProtectedView, self).dispatch(*args, **kwargs)
|
|
|
|
In this example, every instance of ``ProtectedView`` will have
|
|
login protection.
|
|
|
|
.. note::
|
|
|
|
``method_decorator`` passes ``*args`` and ``**kwargs``
|
|
as parameters to the decorated method on the class. If your method
|
|
does not accept a compatible set of parameters it will raise a
|
|
``TypeError`` exception.
|