forked from orbit-oss/flask
Merge pull request #2252 from davidism/method-view-inheritance
Continue #1936: Add the ability to combine MethodViews
This commit is contained in:
commit
9b892e2225
3 changed files with 66 additions and 18 deletions
2
CHANGES
2
CHANGES
|
|
@ -24,8 +24,10 @@ Major release, unreleased
|
|||
- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable
|
||||
adding OPTIONS method when the ``view_func`` argument is not a class.
|
||||
(`#1489`_).
|
||||
- ``MethodView`` can inherit method handlers from base classes. (`#1936`_)
|
||||
|
||||
.. _#1489: https://github.com/pallets/flask/pull/1489
|
||||
.. _#1936: https://github.com/pallets/flask/pull/1936
|
||||
.. _#2017: https://github.com/pallets/flask/pull/2017
|
||||
.. _#2223: https://github.com/pallets/flask/pull/2223
|
||||
|
||||
|
|
|
|||
|
|
@ -103,33 +103,34 @@ class View(object):
|
|||
|
||||
|
||||
class MethodViewType(type):
|
||||
"""Metaclass for :class:`MethodView` that determines what methods the view
|
||||
defines.
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, d):
|
||||
super(MethodViewType, cls).__init__(name, bases, d)
|
||||
|
||||
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 in d:
|
||||
if key in http_method_funcs:
|
||||
methods = set()
|
||||
|
||||
for key in http_method_funcs:
|
||||
if hasattr(cls, key):
|
||||
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 base class or another subclass of a base method view
|
||||
# that does not introduce new methods).
|
||||
|
||||
# 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 base class
|
||||
# or another subclass of a base method view that does not introduce
|
||||
# new methods.
|
||||
if methods:
|
||||
rv.methods = sorted(methods)
|
||||
return rv
|
||||
cls.methods = methods
|
||||
|
||||
|
||||
class MethodView(with_metaclass(MethodViewType, 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 it will respond to ``'GET'`` requests and
|
||||
the :meth:`dispatch_request` implementation will automatically
|
||||
forward your request to that. Also :attr:`options` is set for you
|
||||
automatically::
|
||||
"""A class-based view that dispatches request methods to the corresponding
|
||||
class methods. For example, if you implement a ``get`` method, it will be
|
||||
used to handle ``GET`` requests. ::
|
||||
|
||||
class CounterAPI(MethodView):
|
||||
|
||||
def get(self):
|
||||
return session.get('counter', 0)
|
||||
|
||||
|
|
@ -139,11 +140,14 @@ class MethodView(with_metaclass(MethodViewType, View)):
|
|||
|
||||
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
|
||||
"""
|
||||
|
||||
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, 'Unimplemented method %r' % request.method
|
||||
return meth(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -160,3 +160,45 @@ def test_endpoint_override():
|
|||
|
||||
# But these tests should still pass. We just log a warning.
|
||||
common_test(app)
|
||||
|
||||
def test_multiple_inheritance():
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
class GetView(flask.views.MethodView):
|
||||
def get(self):
|
||||
return 'GET'
|
||||
|
||||
class DeleteView(flask.views.MethodView):
|
||||
def delete(self):
|
||||
return 'DELETE'
|
||||
|
||||
class GetDeleteView(GetView, DeleteView):
|
||||
pass
|
||||
|
||||
app.add_url_rule('/', view_func=GetDeleteView.as_view('index'))
|
||||
|
||||
c = app.test_client()
|
||||
assert c.get('/').data == b'GET'
|
||||
assert c.delete('/').data == b'DELETE'
|
||||
assert sorted(GetDeleteView.methods) == ['DELETE', 'GET']
|
||||
|
||||
def test_remove_method_from_parent():
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
class GetView(flask.views.MethodView):
|
||||
def get(self):
|
||||
return 'GET'
|
||||
|
||||
class OtherView(flask.views.MethodView):
|
||||
def post(self):
|
||||
return 'POST'
|
||||
|
||||
class View(GetView, OtherView):
|
||||
methods = ['GET']
|
||||
|
||||
app.add_url_rule('/', view_func=View.as_view('index'))
|
||||
|
||||
c = app.test_client()
|
||||
assert c.get('/').data == b'GET'
|
||||
assert c.post('/').status_code == 405
|
||||
assert sorted(View.methods) == ['GET']
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue