Skip to content
This repository
Newer
Older
100644 147 lines (116 sloc) 5.414 kb
dcf21989 »
2011-06-28 Added class based views
1 # -*- coding: utf-8 -*-
2 """
3 flask.views
4 ~~~~~~~~~~~
5
6 This module provides class based views inspired by the ones in Django.
7
8 :copyright: (c) 2011 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
10 """
11 from .globals import request
12
13
14 http_method_funcs = frozenset(['get', 'post', 'head', 'options',
15 'delete', 'put', 'trace'])
16
17
18
19 class View(object):
20 """Alternative way to use view functions. A subclass has to implement
21 :meth:`dispatch_request` which is called with the view arguments from
22 the URL routing system. If :attr:`methods` is provided the methods
23 do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
24 method explicitly::
25
26 class MyView(View):
27 methods = ['GET']
28
29 def dispatch_request(self, name):
30 return 'Hello %s!' % name
31
32 app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
ef0f626f »
2011-08-25 Added flask.views.View.decorators to automatically decorate class bas…
33
34 When you want to decorate a pluggable view you will have to either do that
35 when the view function is created (by wrapping the return value of
36 :meth:`as_view`) or you can use the :attr:`decorators` attribute::
37
38 class SecretView(View):
39 methods = ['GET']
40 decorators = [superuser_required]
41
42 def dispatch_request(self):
43 ...
44
45 The decorators stored in the decorators list are applied one after another
46 when the view function is created. Note that you can *not* use the class
47 based decorators since those would decorate the view class and not the
48 generated view function!
dcf21989 »
2011-06-28 Added class based views
49 """
50
ef0f626f »
2011-08-25 Added flask.views.View.decorators to automatically decorate class bas…
51 #: A for which methods this pluggable view can handle.
dcf21989 »
2011-06-28 Added class based views
52 methods = None
53
ef0f626f »
2011-08-25 Added flask.views.View.decorators to automatically decorate class bas…
54 #: The canonical way to decorate class based views is to decorate the
55 #: return value of as_view(). However since this moves parts of the
56 #: logic from the class declaration to the place where it's hooked
57 #: into the routing system.
58 #:
59 #: You can place one or more decorators in this list and whenever the
60 #: view function is created the result is automatically decorated.
61 #:
62 #: .. versionadded:: 0.8
63 decorators = []
64
dcf21989 »
2011-06-28 Added class based views
65 def dispatch_request(self):
775caf72 »
2011-06-28 Improved cbv and added tests
66 """Subclasses have to override this method to implement the
67 actual view functionc ode. This method is called with all
68 the arguments from the URL rule.
69 """
dcf21989 »
2011-06-28 Added class based views
70 raise NotImplementedError()
71
72 @classmethod
73 def as_view(cls, name, *class_args, **class_kwargs):
74 """Converts the class into an actual view function that can be
75 used with the routing system. What it does internally is generating
76 a function on the fly that will instanciate the :class:`View`
77 on each request and call the :meth:`dispatch_request` method on it.
78
79 The arguments passed to :meth:`as_view` are forwarded to the
80 constructor of the class.
81 """
82 def view(*args, **kwargs):
775caf72 »
2011-06-28 Improved cbv and added tests
83 self = view.view_class(*class_args, **class_kwargs)
dcf21989 »
2011-06-28 Added class based views
84 return self.dispatch_request(*args, **kwargs)
ef0f626f »
2011-08-25 Added flask.views.View.decorators to automatically decorate class bas…
85
86 if cls.decorators:
87 view.__name__ = name
88 view.__module__ = cls.__module__
89 for decorator in cls.decorators:
90 view = decorator(view)
91
775caf72 »
2011-06-28 Improved cbv and added tests
92 # we attach the view class to the view function for two reasons:
93 # first of all it allows us to easily figure out what class based
94 # view this thing came from, secondly it's also used for instanciating
95 # the view class so you can actually replace it with something else
96 # for testing purposes and debugging.
97 view.view_class = cls
dcf21989 »
2011-06-28 Added class based views
98 view.__name__ = name
99 view.__doc__ = cls.__doc__
100 view.__module__ = cls.__module__
101 view.methods = cls.methods
102 return view
103
104
105 class MethodViewType(type):
106
107 def __new__(cls, name, bases, d):
108 rv = type.__new__(cls, name, bases, d)
775caf72 »
2011-06-28 Improved cbv and added tests
109 if 'methods' not in d:
110 methods = set(rv.methods or [])
dcf21989 »
2011-06-28 Added class based views
111 for key, value in d.iteritems():
112 if key in http_method_funcs:
775caf72 »
2011-06-28 Improved cbv and added tests
113 methods.add(key.upper())
dcf21989 »
2011-06-28 Added class based views
114 # if we have no method at all in there we don't want to
115 # add a method list. (This is for instance the case for
116 # the baseclass or another subclass of a base method view
117 # that does not introduce new methods).
118 if methods:
775caf72 »
2011-06-28 Improved cbv and added tests
119 rv.methods = sorted(methods)
dcf21989 »
2011-06-28 Added class based views
120 return rv
121
122
123 class MethodView(View):
124 """Like a regular class based view but that dispatches requests to
125 particular methods. For instance if you implement a method called
126 :meth:`get` it means you will response to ``'GET'`` requests and
127 the :meth:`dispatch_request` implementation will automatically
128 forward your request to that. Also :attr:`options` is set for you
129 automatically::
130
131 class CounterAPI(MethodView):
132
133 def get(self):
134 return session.get('counter', 0)
135
136 def post(self):
137 session['counter'] = session.get('counter', 0) + 1
138 return 'OK'
139
140 app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
141 """
142 __metaclass__ = MethodViewType
143
144 def dispatch_request(self, *args, **kwargs):
145 meth = getattr(self, request.method.lower(), None)
146 assert meth is not None, 'Not implemented method'
147 return meth(*args, **kwargs)
Something went wrong with that request. Please try again.