<p dir="auto">I am trying to get the <code class="notranslate">route</code> decorator to work with the <code class="notranslate">MethodView</code>.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Blueprint
from flask.views import MethodView

api = Blueprint(&quot;api&quot;, __name__)

@api.route(&quot;/users&quot;)
class UserAPI(MethodView):

    def get(self):
        pass

    def post(self):
        pass"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Blueprint</span>
<span class="pl-k">from</span> <span class="pl-s1">flask</span>.<span class="pl-s1">views</span> <span class="pl-k">import</span> <span class="pl-v">MethodView</span>

<span class="pl-s1">api</span> <span class="pl-c1">=</span> <span class="pl-v">Blueprint</span>(<span class="pl-s">"api"</span>, <span class="pl-s1">__name__</span>)

<span class="pl-en">@<span class="pl-s1">api</span>.<span class="pl-en">route</span>(<span class="pl-s">"/users"</span>)</span>
<span class="pl-k">class</span> <span class="pl-v">UserAPI</span>(<span class="pl-v">MethodView</span>):

    <span class="pl-k">def</span> <span class="pl-en">get</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">pass</span>

    <span class="pl-k">def</span> <span class="pl-en">post</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">pass</span></pre></div>
<p dir="auto">I am not sure if this is currently supported but it would be a good plus.</p>
<h3 dir="auto">TL;DR:</h3>
<ul dir="auto">
<li>Being able to use a Blueprint's <code class="notranslate">@pb.route</code> decorator on a <code class="notranslate">MethodView</code> class can help made code more readable</li>
<li>It should be a pretty trivial fix.</li>
<li>I'm working on tests and a merge request right now.</li>
</ul>
<h3 dir="auto">Expected Behavior</h3>
<p dir="auto">Users should be able to combine blueprints with <code class="notranslate">MethodView</code>.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Blueprint
from flask.views import MethodView

api = Blueprint(&quot;api&quot;, __name__, url_prefix=&quot;/api/v1&quot;)

@api.route(&quot;/users&quot;)
@api.route(&quot;/users/&lt;user_id&gt;&quot;)
class UserAPI(MethodView):

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

    def post(self, user_id):
        return f&quot;POST {user_id}&quot;

    def delete(self, user_id):
        return f&quot;DELETE {user_id}&quot;

    def put(self, user_id):
        return f&quot;PUT {user_id}&quot;

    def PATCH(self, user_id):
        return f&quot;PATCH {user_id}&quot;"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Blueprint</span>
<span class="pl-k">from</span> <span class="pl-s1">flask</span>.<span class="pl-s1">views</span> <span class="pl-k">import</span> <span class="pl-v">MethodView</span>

<span class="pl-s1">api</span> <span class="pl-c1">=</span> <span class="pl-v">Blueprint</span>(<span class="pl-s">"api"</span>, <span class="pl-s1">__name__</span>, <span class="pl-s1">url_prefix</span><span class="pl-c1">=</span><span class="pl-s">"/api/v1"</span>)

<span class="pl-en">@<span class="pl-s1">api</span>.<span class="pl-en">route</span>(<span class="pl-s">"/users"</span>)</span>
<span class="pl-en">@<span class="pl-s1">api</span>.<span class="pl-en">route</span>(<span class="pl-s">"/users/&lt;user_id&gt;"</span>)</span>
<span class="pl-k">class</span> <span class="pl-v">UserAPI</span>(<span class="pl-v">MethodView</span>):

    <span class="pl-k">def</span> <span class="pl-en">get</span>(<span class="pl-s1">self</span>, <span class="pl-s1">user_id</span><span class="pl-c1">=</span><span class="pl-c1">None</span>):
        <span class="pl-k">if</span> <span class="pl-s1">user_id</span> <span class="pl-c1">is</span> <span class="pl-c1">None</span>:
            <span class="pl-k">return</span> <span class="pl-s">"list of all users"</span>
        <span class="pl-k">else</span>:
            <span class="pl-k">return</span> <span class="pl-s">f"just user <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_id</span><span class="pl-kos">}</span></span>"</span>

    <span class="pl-k">def</span> <span class="pl-en">post</span>(<span class="pl-s1">self</span>, <span class="pl-s1">user_id</span>):
        <span class="pl-k">return</span> <span class="pl-s">f"POST <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_id</span><span class="pl-kos">}</span></span>"</span>

    <span class="pl-k">def</span> <span class="pl-en">delete</span>(<span class="pl-s1">self</span>, <span class="pl-s1">user_id</span>):
        <span class="pl-k">return</span> <span class="pl-s">f"DELETE <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_id</span><span class="pl-kos">}</span></span>"</span>

    <span class="pl-k">def</span> <span class="pl-en">put</span>(<span class="pl-s1">self</span>, <span class="pl-s1">user_id</span>):
        <span class="pl-k">return</span> <span class="pl-s">f"PUT <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_id</span><span class="pl-kos">}</span></span>"</span>

    <span class="pl-k">def</span> <span class="pl-v">PATCH</span>(<span class="pl-s1">self</span>, <span class="pl-s1">user_id</span>):
        <span class="pl-k">return</span> <span class="pl-s">f"PATCH <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_id</span><span class="pl-kos">}</span></span>"</span></pre></div>
<h3 dir="auto">Actual Behavior</h3>
<p dir="auto">Using a blueprint's <code class="notranslate">@route</code> decorator on a <code class="notranslate">MethodView</code> results in <code class="notranslate">TypeError</code> is raised:</p>
<div class="highlight highlight-text-python-traceback notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="Traceback (most recent call last):
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 2449, in wsgi_app
    response = self.handle_exception(e)
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/_compat.py&quot;, line 39, in reraise
    raise value
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 1952, in full_dispatch_request
    return self.finalize_request(rv)
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 1967, in finalize_request
    response = self.make_response(rv)
  File &quot;/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py&quot;, line 2130, in make_response
    &quot; {rv.__class__.__name__}.&quot;.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."><pre class="notranslate">Traceback (most recent call last):
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">2463</span>, in <span class="pl-en">__call__</span>
    <span class="pl-k">return</span> <span class="pl-c1">self</span>.wsgi_app(environ, start_response)
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">2449</span>, in <span class="pl-en">wsgi_app</span>
    response <span class="pl-k">=</span> <span class="pl-c1">self</span>.handle_exception(e)
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">1866</span>, in <span class="pl-en">handle_exception</span>
    reraise(exc_type, exc_value, tb)
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/_compat.py"</span>, line <span class="pl-c1">39</span>, in <span class="pl-en">reraise</span>
    <span class="pl-k">raise</span> value
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">2446</span>, in <span class="pl-en">wsgi_app</span>
    response <span class="pl-k">=</span> <span class="pl-c1">self</span>.full_dispatch_request()
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">1952</span>, in <span class="pl-en">full_dispatch_request</span>
    <span class="pl-k">return</span> <span class="pl-c1">self</span>.finalize_request(rv)
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">1967</span>, in <span class="pl-en">finalize_request</span>
    response <span class="pl-k">=</span> <span class="pl-c1">self</span>.make_response(rv)
  File <span class="pl-s">"/c/gitlab/mocvd/sic-inventory/.venv/lib/python3.6/site-packages/flask/app.py"</span>, line <span class="pl-c1">2130</span>, in <span class="pl-en">make_response</span>
    <span class="pl-s"><span class="pl-pds">"</span> <span class="pl-c1">{rv.__class__.__name__}</span>.<span class="pl-pds">"</span></span>.format(<span class="pl-v">rv</span><span class="pl-k">=</span>rv)
<span class="pl-en">TypeError</span>: <span class="pl-s">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.</span></pre></div>
<p dir="auto">Instead, users need to manually add rules to the app after the fact, which can be hard to read and requires that the <code class="notranslate">app</code> instance be available in the module.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="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/&lt;int:user_id&gt;', view_func=user_view,
                 methods=['GET', 'PUT', 'PATCH', 'DELETE'])"><pre class="notranslate"><span class="pl-s1">user_view</span> <span class="pl-c1">=</span> <span class="pl-v">UserAPI</span>.<span class="pl-en">as_view</span>(<span class="pl-s">'user_api'</span>)
<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/users/'</span>, <span class="pl-s1">defaults</span><span class="pl-c1">=</span>{<span class="pl-s">'user_id'</span>: <span class="pl-c1">None</span>},
                 <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-s1">user_view</span>, <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'GET'</span>,])
<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/users/'</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-s1">user_view</span>, <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'POST'</span>,])
<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/users/&lt;int:user_id&gt;'</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-s1">user_view</span>,
                 <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'GET'</span>, <span class="pl-s">'PUT'</span>, <span class="pl-s">'PATCH'</span>, <span class="pl-s">'DELETE'</span>])</pre></div>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Python version: 3.6.8</li>
<li>Flask version: 1.1.1</li>
<li>Werkzeug version: 0.16.0</li>
</ul>
<h3 dir="auto">The Fix</h3>
<p dir="auto">Based on my testing is <em>should</em> be relatively trivial to support this. All that needs to be done is edit <code class="notranslate">Blueprint.route</code>:</p>
<div class="highlight highlight-source-diff notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="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 @@
 &quot;&quot;&quot;
 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(&quot;endpoint&quot;, 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"><pre class="notranslate"><span class="pl-c1">diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py</span>
index 8978104d..f6f62dd6 100644
<span class="pl-md">--- a/src/flask/blueprints.py</span>
<span class="pl-mi1">+++ b/src/flask/blueprints.py</span>
<span class="pl-mdr">@@ -11,6 +11,8 @@</span>
 """
 from functools import update_wrapper

<span class="pl-mi1"><span class="pl-mi1">+</span>from .views import MethodViewType</span>
<span class="pl-mi1"><span class="pl-mi1">+</span></span>
 from .helpers import _endpoint_from_view_func
 from .helpers import _PackageBoundObject

<span class="pl-mdr">@@ -276,6 +278,11 @@</span> class Blueprint(_PackageBoundObject):

         def decorator(f):
             endpoint = options.pop("endpoint", f.__name__)
<span class="pl-mi1"><span class="pl-mi1">+</span></span>
<span class="pl-mi1"><span class="pl-mi1">+</span>            # Support decorating MethodView classes</span>
<span class="pl-mi1"><span class="pl-mi1">+</span>            if isinstance(f, MethodViewType):</span>
<span class="pl-mi1"><span class="pl-mi1">+</span>                f = f.as_view(endpoint)</span>
<span class="pl-mi1"><span class="pl-mi1">+</span></span>
             self.add_url_rule(rule, endpoint, f, **options)
             return f</pre></div>
<p dir="auto"><strong>Note:</strong> I originally saw this feature in the <code class="notranslate">marshmallow-code/flask-smorest</code> library (originally <code class="notranslate">Nobatek/flask-rest-api</code>) <a href="https://github.com/marshmallow-code/flask-smorest/blob/cc618f74abc2035d9f2904efed32cfc3bafad3ab/flask_smorest/blueprint.py#L90">flask_smorest/blueprint.py</a>.<br>
The above code is taken almost verbatim from that library.</p>