================= 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`. .. 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 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`. 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.