{{{#!python
decorators = [never_cache, login_required]
@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
}}}
However, it appears there is a slight difference in behavior. For example:
{{{#!python
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
def my_decorator(func):
def new_func(*args, **kwargs):
func(*args, **kwargs)
return new_func
@method_decorator(my_decorator, name='dispatch')
@method_decorator(csrf_exempt, name='dispatch')
class View1:
def dispatch(self):
pass
@method_decorator([my_decorator, csrf_exempt], name='dispatch')
class View2:
def dispatch(self):
pass
print(hasattr(View1.dispatch, 'csrf_exempt'))
print(hasattr(View2.dispatch, 'csrf_exempt'))
}}}
results in the output--
{{{
True
False
}}}
It appears this is because `method_decorator()` takes a
[https://github.com/django/django/blob/ee7f51c66dfc1700ff065dfeb5fe2388cc2e9619/django/utils/decorators.py#L51
short cut] when processing a list. It doesn't carry over the attributes of
the decorated function like it does in the normal case.
--
Ticket URL: <https://code.djangoproject.com/ticket/29253>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Comment (by Chris Jerdonek):
The behavior difference described in this ticket is actually noted in the
discussion of the patch
[https://github.com/django/django/pull/5136#discussion_r37068407 here],
but it was thought not important. I do think it's important though
because, for example, it can cause decorators to have or not have an
effect depending on which invocation style is used.
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:1>
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:2>
* owner: nobody => Chris Jerdonek
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:3>
* has_patch: 0 => 1
Comment:
I prepared a pull request here: https://github.com/django/django/pull/9819
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:4>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"fdc936c9130cf4fb5d59869674b9a31cc79a7999" fdc936c]:
{{{
#!CommitTicketReference repository=""
revision="fdc936c9130cf4fb5d59869674b9a31cc79a7999"
Fixed #29253 -- Made method_decorator(list) copy attributes.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:5>
Comment (by Tim Graham):
[https://github.com/django/django/pull/10091 PR 10091] fixes a regression
where `@method_decorator(transaction.non_atomic_requests)` crashes.
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:6>
Comment (by Tim Graham <timograham@…>):
In [changeset:"f434f5b84f7fcea9a76a551621ecce70786e2899" f434f5b]:
{{{
#!CommitTicketReference repository=""
revision="f434f5b84f7fcea9a76a551621ecce70786e2899"
Refs #29253 -- Fixed method_decorator() crash if decorator sets a new
attribute.
Regression in fdc936c9130cf4fb5d59869674b9a31cc79a7999.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:7>
Comment (by Tim Graham <timograham@…>):
In [changeset:"da46599143408be58c1845b8c82cacbae0bb56c0" da46599]:
{{{
#!CommitTicketReference repository=""
revision="da46599143408be58c1845b8c82cacbae0bb56c0"
[2.1.x] Refs #29253 -- Fixed method_decorator() crash if decorator sets a
new attribute.
Regression in fdc936c9130cf4fb5d59869674b9a31cc79a7999.
Backport of f434f5b84f7fcea9a76a551621ecce70786e2899 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/29253#comment:8>