Skip to main content

Pluggable Class-Based Views

Pluggable class-based views provide an alternative to function-based views, allowing for reusable and inheritable view logic. By using the View and MethodView classes in flask.views, you can organize code into classes that handle different HTTP methods or share common functionality through standard Python inheritance.

The View Base Class

The foundation of class-based views is the View class. To implement a view, you subclass View and override the dispatch_request method. This method acts as the entry point for the request and must return a valid response.

from flask.views import View

class MyView(View):
def dispatch_request(self):
return "Hello from a class-based view!"

Registration with as_view

Classes cannot be passed directly to app.add_url_rule. Instead, you must use the as_view class method to convert the class into a view function. The first argument to as_view is the name of the endpoint.

app.add_url_rule("/my-view", view_func=MyView.as_view("my_view_endpoint"))

Constructor Injection

One of the primary advantages of class-based views is the ability to configure them during registration. Any additional arguments passed to as_view are forwarded to the class's __init__ method.

As seen in tests/type_check/typing_route.py, this allows for generic views that can be reused with different configurations:

class RenderTemplateView(View):
def __init__(self, template_name: str) -> None:
self.template_name = template_name

def dispatch_request(self) -> str:
# Logic to render self.template_name
return f"Rendering {self.template_name}"

app.add_url_rule(
"/about",
view_func=RenderTemplateView.as_view("about_page", template_name="about.html"),
)

RESTful Dispatching with MethodView

The MethodView class, a subclass of View, is specifically designed for RESTful APIs. It automatically dispatches requests to methods named after the HTTP verbs (e.g., get(), post(), put(), delete()).

In src/flask/views.py, MethodView uses __init_subclass__ to automatically populate the methods attribute based on which methods you have implemented.

from flask.views import MethodView

class UserAPI(MethodView):
def get(self, user_id):
return f"User {user_id} details"

def post(self):
return "Create new user"

app.add_url_rule("/users/<int:user_id>", view_func=UserAPI.as_view("users"))

If a HEAD request is received and the class does not implement a head() method, MethodView will automatically fall back to the get() method.

Configuration and Optimization

The View class provides several class-level attributes to control how the view behaves.

Methods and Options

You can explicitly define which HTTP methods the view accepts using the methods attribute. For MethodView, this is handled automatically, but for a standard View, it defaults to ["GET", "HEAD", "OPTIONS"].

class MyPostView(View):
methods = ["POST"]

def dispatch_request(self):
return "Handled POST"

Applying Decorators

To apply decorators to a class-based view, use the decorators class attribute. This is a list of decorators that will be applied to the view function generated by as_view.

Critical Note: Applying a decorator directly to the class using @decorator syntax will not work as expected because it decorates the class itself, not the resulting view function.

# Example from tests/test_views.py
class Index(View):
decorators = [auth_required, log_request]

def dispatch_request(self):
return "Secure Content"

Request Instance Lifecycle

By default, Flask creates a new instance of the view class for every request (init_every_request = True). This allows you to safely store request-specific data on self.

For better performance, if your view does not need to store state on self, you can set init_every_request = False. In this mode, a single instance is created when as_view is called and reused for all requests.

class EfficientView(View):
init_every_request = False # Shared across all requests

def dispatch_request(self):
# Use flask.g for request-specific data instead of self
return "Efficiently handled"

Async Support

Class-based views fully support async methods. When dispatch_request (or the verb methods in MethodView) is asynchronous, Flask uses current_app.ensure_sync to handle the execution.

An example from tests/test_async.py demonstrates an asynchronous MethodView:

class AsyncMethodView(MethodView):
async def get(self):
await asyncio.sleep(0)
return "GET"

async def post(self):
await asyncio.sleep(0)
return "POST"