Browse Source

Added flask.views.View.decorators to automatically decorate class based views.

pull/309/head
Armin Ronacher 14 years ago
parent
commit
ef0f626f0a
  1. 2
      CHANGES
  2. 35
      flask/views.py
  3. 21
      tests/flask_tests.py

2
CHANGES

@ -38,6 +38,8 @@ Relase date to be decided, codename to be chosen.
- Refactored test client internally. The ``APPLICATION_ROOT`` configuration - Refactored test client internally. The ``APPLICATION_ROOT`` configuration
variable as well as ``SERVER_NAME`` are now properly used by the test client variable as well as ``SERVER_NAME`` are now properly used by the test client
as defaults. as defaults.
- Added :attr:`flask.views.View.decorators` to support simpler decorating of
pluggable (class based) views.
Version 0.7.3 Version 0.7.3
------------- -------------

35
flask/views.py

@ -30,10 +30,38 @@ class View(object):
return 'Hello %s!' % name return 'Hello %s!' % name
app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview')) app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
When you want to decorate a pluggable view you will have to either do that
when the view function is created (by wrapping the return value of
:meth:`as_view`) or you can use the :attr:`decorators` attribute::
class SecretView(View):
methods = ['GET']
decorators = [superuser_required]
def dispatch_request(self):
...
The decorators stored in the decorators list are applied one after another
when the view function is created. Note that you can *not* use the class
based decorators since those would decorate the view class and not the
generated view function!
""" """
#: A for which methods this pluggable view can handle.
methods = None methods = None
#: The canonical way to decorate class based views is to decorate the
#: return value of as_view(). However since this moves parts of the
#: logic from the class declaration to the place where it's hooked
#: into the routing system.
#:
#: You can place one or more decorators in this list and whenever the
#: view function is created the result is automatically decorated.
#:
#: .. versionadded:: 0.8
decorators = []
def dispatch_request(self): def dispatch_request(self):
"""Subclasses have to override this method to implement the """Subclasses have to override this method to implement the
actual view functionc ode. This method is called with all actual view functionc ode. This method is called with all
@ -54,6 +82,13 @@ class View(object):
def view(*args, **kwargs): def view(*args, **kwargs):
self = view.view_class(*class_args, **class_kwargs) self = view.view_class(*class_args, **class_kwargs)
return self.dispatch_request(*args, **kwargs) return self.dispatch_request(*args, **kwargs)
if cls.decorators:
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators:
view = decorator(view)
# we attach the view class to the view function for two reasons: # we attach the view class to the view function for two reasons:
# first of all it allows us to easily figure out what class based # first of all it allows us to easily figure out what class based
# view this thing came from, secondly it's also used for instanciating # view this thing came from, secondly it's also used for instanciating

21
tests/flask_tests.py

@ -2258,6 +2258,27 @@ class ViewTestCase(FlaskTestCase):
meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow']) meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
self.assertEqual(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST']) self.assertEqual(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'])
def test_view_decorators(self):
app = flask.Flask(__name__)
def add_x_parachute(f):
def new_function(*args, **kwargs):
resp = flask.make_response(f(*args, **kwargs))
resp.headers['X-Parachute'] = 'awesome'
return resp
return new_function
class Index(flask.views.View):
decorators = [add_x_parachute]
def dispatch_request(self):
return 'Awesome'
app.add_url_rule('/', view_func=Index.as_view('index'))
c = app.test_client()
rv = c.get('/')
self.assertEqual(rv.headers['X-Parachute'], 'awesome')
self.assertEqual(rv.data, 'Awesome')
class DeprecationsTestCase(FlaskTestCase): class DeprecationsTestCase(FlaskTestCase):

Loading…
Cancel
Save