Skip to main content

Introduction to Class-Based Views

Class-based views in Flask provide an alternative to function-based views, allowing you to use inheritance and mixins to reuse code. When a single view function grows too large because it handles many HTTP methods or complex branching logic, class-based views offer a structured way to organize that code.

The Base View Class

To create a class-based view, you subclass flask.views.View and implement the dispatch_request method. This method acts as the entry point for the request, similar to a standard view function.

To use the class with Flask's routing system, you must convert it into a view function using the as_view class method.

from flask import request
from flask.views import View

class MyView(View):
methods = ["GET", "POST"]

def dispatch_request(self):
if request.method == "POST":
return "Handled POST"
return "Handled GET"

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

Internally, as_view returns a wrapper function. When a request matches the route, this wrapper creates an instance of your class and calls its dispatch_request method. Any URL variables from the route are passed as keyword arguments to dispatch_request.

Method-Based Dispatching

For RESTful APIs where you want different logic for each HTTP verb, flask.views.MethodView is more convenient. It automatically dispatches requests to methods named after the HTTP verbs (e.g., get(), post(), put()).

from flask.views import MethodView

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

def post(self):
return "User created"

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

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

Automatic Method Detection

MethodView uses __init_subclass__ to inspect your class and automatically populate the methods attribute based on which HTTP verb methods you have defined (like get, post, etc.).

If a HEAD request is received but you haven't defined a head() method, Flask automatically falls back to calling your get() method, as seen in the MethodView.dispatch_request implementation:

# From src/flask/views.py
def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue:
meth = getattr(self, request.method.lower(), None)

if meth is None and request.method == "HEAD":
meth = getattr(self, "get", None)

assert meth is not None, f"Unimplemented method {request.method!r}"
return current_app.ensure_sync(meth)(**kwargs)

Customizing View Behavior

Applying Decorators

Because as_view returns a generated function, applying decorators directly to the class (e.g., @login_required) will not work as expected—it would decorate the class itself, not the view function Flask calls. Instead, use the decorators class attribute.

class SecretView(View):
decorators = [auth_required, audit_log]

def dispatch_request(self):
return "Top secret data"

Flask iterates through this list and applies them to the generated view function in order.

Performance and State

By default, Flask creates a new instance of your view class for every request (init_every_request = True). This ensures that storing data on self is safe within a single request but won't leak to others.

If your view has expensive initialization logic that doesn't depend on the request, you can set init_every_request = False. This causes Flask to reuse a single instance for all requests.

class EfficientView(View):
init_every_request = False

def __init__(self):
self.shared_resource = load_heavy_resource()

def dispatch_request(self):
# Do NOT store request-specific data on self here!
# Use flask.g for request-global data instead.
return "Handled"

Async Support

Flask class-based views support async methods out of the box. Both View.as_view and MethodView.dispatch_request use current_app.ensure_sync to execute your methods, allowing you to define async def dispatch_request or async def get without additional configuration.

class AsyncUserAPI(MethodView):
async def get(self, user_id):
user = await database.fetch_user(user_id)
return user.to_dict()

Inheritance and Mixins

One of the primary advantages of CBVs is inheritance. You can define a base class with common logic and extend it for specific endpoints.

class BaseListView(View):
def get_template_name(self):
raise NotImplementedError()

def dispatch_request(self):
items = self.get_items()
return render_template(self.get_template_name(), items=items)

class UserListView(BaseListView):
def get_items(self):
return User.query.all()

def get_template_name(self):
return "users.html"

When using MethodView, subclasses inherit the methods defined in parent classes. If you define a get method in a base class and a post method in a subclass, the resulting view will support both GET and POST.