Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/websdk/flask/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'websdk/flask/views.py')
-rw-r--r--websdk/flask/views.py151
1 files changed, 151 insertions, 0 deletions
diff --git a/websdk/flask/views.py b/websdk/flask/views.py
new file mode 100644
index 0000000..be718cc
--- /dev/null
+++ b/websdk/flask/views.py
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.views
+ ~~~~~~~~~~~
+
+ This module provides class based views inspired by the ones in Django.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from .globals import request
+
+
+http_method_funcs = frozenset(['get', 'post', 'head', 'options',
+ 'delete', 'put', 'trace'])
+
+
+
+class View(object):
+ """Alternative way to use view functions. A subclass has to implement
+ :meth:`dispatch_request` which is called with the view arguments from
+ the URL routing system. If :attr:`methods` is provided the methods
+ do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
+ method explicitly::
+
+ class MyView(View):
+ methods = ['GET']
+
+ 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 function code. This method is called with all
+ the arguments from the URL rule.
+ """
+ raise NotImplementedError()
+
+ @classmethod
+ def as_view(cls, name, *class_args, **class_kwargs):
+ """Converts the class into an actual view function that can be
+ used with the routing system. What it does internally is generating
+ a function on the fly that will instanciate the :class:`View`
+ on each request and call the :meth:`dispatch_request` method on it.
+
+ The arguments passed to :meth:`as_view` are forwarded to the
+ constructor of the class.
+ """
+ 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
+ # the view class so you can actually replace it with something else
+ # for testing purposes and debugging.
+ view.view_class = cls
+ view.__name__ = name
+ view.__doc__ = cls.__doc__
+ view.__module__ = cls.__module__
+ view.methods = cls.methods
+ return view
+
+
+class MethodViewType(type):
+
+ def __new__(cls, name, bases, d):
+ rv = type.__new__(cls, name, bases, d)
+ if 'methods' not in d:
+ methods = set(rv.methods or [])
+ for key, value in d.iteritems():
+ if key in http_method_funcs:
+ methods.add(key.upper())
+ # if we have no method at all in there we don't want to
+ # add a method list. (This is for instance the case for
+ # the baseclass or another subclass of a base method view
+ # that does not introduce new methods).
+ if methods:
+ rv.methods = sorted(methods)
+ return rv
+
+
+class MethodView(View):
+ """Like a regular class based view but that dispatches requests to
+ particular methods. For instance if you implement a method called
+ :meth:`get` it means you will response to ``'GET'`` requests and
+ the :meth:`dispatch_request` implementation will automatically
+ forward your request to that. Also :attr:`options` is set for you
+ automatically::
+
+ class CounterAPI(MethodView):
+
+ def get(self):
+ return session.get('counter', 0)
+
+ def post(self):
+ session['counter'] = session.get('counter', 0) + 1
+ return 'OK'
+
+ app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
+ """
+ __metaclass__ = MethodViewType
+
+ def dispatch_request(self, *args, **kwargs):
+ meth = getattr(self, request.method.lower(), None)
+ # if the request method is HEAD and we don't have a handler for it
+ # retry with GET
+ if meth is None and request.method == 'HEAD':
+ meth = getattr(self, 'get', None)
+ assert meth is not None, 'Not implemented method %r' % request.method
+ return meth(*args, **kwargs)