Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added flask.views.View.decorators to automatically decorate class bas…

…ed views.
  • Loading branch information...
commit ef0f626f0a916fdced42e05aa2f58dede6c660fd 1 parent 8340d3c
@mitsuhiko authored
Showing with 58 additions and 0 deletions.
  1. +2 −0  CHANGES
  2. +35 −0 flask/views.py
  3. +21 −0 tests/flask_tests.py
View
2  CHANGES
@@ -38,6 +38,8 @@ Relase date to be decided, codename to be chosen.
- Refactored test client internally. The ``APPLICATION_ROOT`` configuration
variable as well as ``SERVER_NAME`` are now properly used by the test client
as defaults.
+- Added :attr:`flask.views.View.decorators` to support simpler decorating of
+ pluggable (class based) views.
Version 0.7.3
-------------
View
35 flask/views.py
@@ -30,10 +30,38 @@ def dispatch_request(self, name):
return 'Hello %s!' % name
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
+ #: 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):
"""Subclasses have to override this method to implement the
actual view functionc ode. This method is called with all
@@ -54,6 +82,13 @@ def as_view(cls, name, *class_args, **class_kwargs):
def view(*args, **kwargs):
self = view.view_class(*class_args, **class_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:
# 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
21 tests/flask_tests.py
@@ -2258,6 +2258,27 @@ def delete(self):
meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
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):
Please sign in to comment.
Something went wrong with that request. Please try again.