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