Skip to content

Commit 648344d

Browse files
committed
use mro to collect methods
ignore methods attr unless explicitly set add changelog
1 parent 0d9d3d8 commit 648344d

File tree

2 files changed

+24
-22
lines changed

2 files changed

+24
-22
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ Major release, unreleased
2424
- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable
2525
adding OPTIONS method when the ``view_func`` argument is not a class.
2626
(`#1489`_).
27+
- ``MethodView`` can inherit method handlers from base classes. (`#1936`_)
2728

2829
.. _#1489: https://github.com/pallets/flask/pull/1489
30+
.. _#1936: https://github.com/pallets/flask/pull/1936
2931
.. _#2017: https://github.com/pallets/flask/pull/2017
3032
.. _#2223: https://github.com/pallets/flask/pull/2223
3133

flask/views.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -102,38 +102,35 @@ def view(*args, **kwargs):
102102
return view
103103

104104

105-
def get_methods(cls):
106-
return getattr(cls, 'methods', []) or []
107-
108-
109105
class MethodViewType(type):
106+
"""Metaclass for :class:`MethodView` that determines what methods the view
107+
defines.
108+
"""
109+
110+
def __init__(cls, name, bases, d):
111+
super(MethodViewType, cls).__init__(name, bases, d)
110112

111-
def __new__(cls, name, bases, d):
112-
rv = type.__new__(cls, name, bases, d)
113113
if 'methods' not in d:
114-
methods = set(m for b in bases for m in get_methods(b))
115-
for key in d:
116-
if key in http_method_funcs:
114+
methods = set()
115+
116+
for key in http_method_funcs:
117+
if hasattr(cls, key):
117118
methods.add(key.upper())
118-
# If we have no method at all in there we don't want to
119-
# add a method list. (This is for instance the case for
120-
# the base class or another subclass of a base method view
121-
# that does not introduce new methods).
119+
120+
# If we have no method at all in there we don't want to add a
121+
# method list. This is for instance the case for the base class
122+
# or another subclass of a base method view that does not introduce
123+
# new methods.
122124
if methods:
123-
rv.methods = sorted(methods)
124-
return rv
125+
cls.methods = methods
125126

126127

127128
class MethodView(with_metaclass(MethodViewType, View)):
128-
"""Like a regular class-based view but that dispatches requests to
129-
particular methods. For instance if you implement a method called
130-
:meth:`get` it means it will respond to ``'GET'`` requests and
131-
the :meth:`dispatch_request` implementation will automatically
132-
forward your request to that. Also :attr:`options` is set for you
133-
automatically::
129+
"""A class-based view that dispatches request methods to the corresponding
130+
class methods. For example, if you implement a ``get`` method, it will be
131+
used to handle ``GET`` requests. ::
134132
135133
class CounterAPI(MethodView):
136-
137134
def get(self):
138135
return session.get('counter', 0)
139136
@@ -143,11 +140,14 @@ def post(self):
143140
144141
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
145142
"""
143+
146144
def dispatch_request(self, *args, **kwargs):
147145
meth = getattr(self, request.method.lower(), None)
146+
148147
# If the request method is HEAD and we don't have a handler for it
149148
# retry with GET.
150149
if meth is None and request.method == 'HEAD':
151150
meth = getattr(self, 'get', None)
151+
152152
assert meth is not None, 'Unimplemented method %r' % request.method
153153
return meth(*args, **kwargs)

0 commit comments

Comments
 (0)