Skip to content

Commit 591ad8a

Browse files
committed
Fixed #14512 -- Added documentation on how to apply decorators to class-based generic views. Thanks to Łukasz Rekucki for his work on the issue.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14642 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent ec7c49f commit 591ad8a

File tree

3 files changed

+63
-9
lines changed

3 files changed

+63
-9
lines changed

django/utils/decorators.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
"Functions that help with dynamically creating decorators for views."
22

3-
import types
43
try:
54
from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
65
except ImportError:
76
from django.utils.functional import wraps, update_wrapper, WRAPPER_ASSIGNMENTS # Python 2.4 fallback.
87

8+
class classonlymethod(classmethod):
9+
def __get__(self, instance, owner):
10+
if instance is not None:
11+
raise AttributeError("This method is available only on the view class.")
12+
return super(classonlymethod, self).__get__(instance, owner)
913

1014
def method_decorator(decorator):
1115
"""

django/views/generic/base.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
1-
import copy
21
from django import http
32
from django.core.exceptions import ImproperlyConfigured
43
from django.template import RequestContext, loader
5-
from django.utils.translation import ugettext_lazy as _
64
from django.utils.functional import update_wrapper
75
from django.utils.log import getLogger
6+
from django.utils.decorators import classonlymethod
87

98
logger = getLogger('django.request')
109

11-
class classonlymethod(classmethod):
12-
def __get__(self, instance, owner):
13-
if instance is not None:
14-
raise AttributeError("This method is available only on the view class.")
15-
return super(classonlymethod, self).__get__(instance, owner)
16-
1710
class View(object):
1811
"""
1912
Intentionally simple parent class for all views. Only implements

docs/topics/class-based-views.txt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,3 +537,60 @@ Because of the way that Python resolves method overloading, the local
537537
:func:`render_to_response()` implementation will override the
538538
versions provided by :class:`JSONResponseMixin` and
539539
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
540+
541+
Decorating class-based views
542+
============================
543+
544+
.. highlightlang:: python
545+
546+
The extension of class-based views isn't limited to using mixins. You
547+
can use also use decorators.
548+
549+
Decorating in URLconf
550+
---------------------
551+
552+
The simplest way of decorating class-based views is to decorate the
553+
result of the :meth:`~django.views.generic.base.View.as_view` method.
554+
The easiest place to do this is in the URLconf where you deploy your
555+
view::
556+
557+
from django.contrib.auth.decorators import login_required
558+
from django.views.generic import TemplateView
559+
560+
urlpatterns = patterns('',
561+
(r'^about/',login_required(TemplateView.as_view(template_name="secret.html"))),
562+
)
563+
564+
This approach applies the decorator on a per-instance basis. If you
565+
want every instance of a view to be decorated, you need to take a
566+
different approach.
567+
568+
Decorating the class
569+
--------------------
570+
571+
To decorate every instance of a class-based view, you need to decorate
572+
the class definition itself. To do this you apply the decorator to one
573+
of the view-like methods on the class; that is,
574+
:meth:`~django.views.generic.base.View.dispatch`, or one of the HTTP
575+
methods (:meth:`~django.views.generic.base.View.get`,
576+
:meth:`~django.views.generic.base.View.post` etc).
577+
578+
A method on a class isn't quite the same as a standalone function, so
579+
you can't just apply a function decorator to the method -- you need to
580+
transform it into a method decorator first. The ``method_decorator``
581+
decorator transforms a function decorator into a method decorator so
582+
that it can be used on an instance method.
583+
584+
from django.contrib.auth.decorators import login_required
585+
from django.utils.decorators import method_decorator
586+
from django.views.generic import TemplateView
587+
588+
class ProtectedView(TemplateView):
589+
template_name = 'secret.html'
590+
591+
@method_decorator(login_required)
592+
def dispatch(self, **kwargs):
593+
return super(ProtectedView, self).dispatch(**kwargs)
594+
595+
In this example, every instance of :class:`ProtectedView` will have
596+
login protection.

0 commit comments

Comments
 (0)