forked from orbit-oss/flask
Added flask.views.View.decorators to automatically decorate class based views.
This commit is contained in:
parent
8340d3c9f5
commit
ef0f626f0a
3 changed files with 58 additions and 0 deletions
2
CHANGES
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
|
||||||
-------------
|
-------------
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue