Issue 1:
I am trying to get the route decorator to work with the MethodView.

from flask import Blueprint
from flask.views import MethodView

api = Blueprint("api", __name__)

@api.route("/users")
class UserAPI(MethodView):

def get(self):
pass

def post(self):
pass

I am not sure if this is currently supported but it would be a good plus.

Issue 2:
TL;DR:

Being able to use a Blueprint's @pb.route decorator on a MethodView class can help made code more readable

It should be a pretty trivial fix.

I'm working on tests and a merge request right now.

Expected Behavior

Users should be able to combine blueprints with MethodView.

from flask import Blueprint
from flask.views import MethodView

api = Blueprint("api", __name__, url_prefix="/api/v1")

@api.route("/users")
@api.route("/users/<user_id>")
class UserAPI(MethodView):

def get(self, user_id=None):
if user_id is None:
return "list of all users"
else:
return f"just user {user_id}"

def post(self, user_id):
return f"POST {user_id}"

def delete(self, user_id):
return f"DELETE {user_id}"

def put(self, user_id):
return f"PUT {user_id}"

def PATCH(self, user_id):
return f"PATCH {user_id}"

Actual Behavior

Using a blueprint's @route decorator on a MethodView results in TypeError is raised:

Traceback (most recent call last):
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 2463, in __call__
return self.wsgi_app(environ, start_response)
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 2449, in wsgi_app
response = self.handle_exception(e)
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 1866, in handle_exception
reraise(exc_type, exc_value, tb)
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
return self.finalize_request(rv)
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 1967, in finalize_request
response = self.make_response(rv)
File "/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py", line 2130, in make_response
" {rv.__class__.__name__}.".format(rv=rv)
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a UserAPI.

Instead, users need to manually add rules to the app after the fact, which can be hard to read and requires that the app instance be available in the module.

user_view = UserAPI.as_view('user_api')
app.add_url_rule('/users/', defaults={'user_id': None},
view_func=user_view, methods=['GET',])
app.add_url_rule('/users/', view_func=user_view, methods=['POST',])
app.add_url_rule('/users/<int:user_id>', view_func=user_view,
methods=['GET', 'PUT', 'PATCH', 'DELETE'])

Environment

Python version: 3.6.8

Flask version: 1.1.1

Werkzeug version: 0.16.0

The Fix

Based on my testing is should be relatively trivial to support this. All that needs to be done is edit Blueprint.route:

diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py
index 8978104d..f6f62dd6 100644
--- a/src/flask/blueprints.py
+++ b/src/flask/blueprints.py
@@ -11,6 +11,8 @@
"""
from functools import update_wrapper

+from .views import MethodViewType
+
from .helpers import _endpoint_from_view_func
from .helpers import _PackageBoundObject

@@ -276,6 +278,11 @@ class Blueprint(_PackageBoundObject):

def decorator(f):
endpoint = options.pop("endpoint", f.__name__)
+
+ # Support decorating MethodView classes
+ if isinstance(f, MethodViewType):
+ f = f.as_view(endpoint)
+
self.add_url_rule(rule, endpoint, f, **options)
return f

Note: I originally saw this feature in the marshmallow-code/flask-smorest library (originally Nobatek/flask-rest-api) flask_smorest/blueprint.py.

The above code is taken almost verbatim from that library.