Skip to main content

Class-Based View Dispatching

The View class in django.views.generic.base serves as the fundamental building block for all class-based views (CBVs). Its primary responsibility is to provide a standardized mechanism for dispatching incoming HTTP requests to specific handler methods based on the HTTP verb (e.g., GET, POST).

The Request Lifecycle

When a request hits a URL associated with a class-based view, it follows a specific execution path through the View class.

1. Entry Point: as_view()

The as_view() method is a class method that acts as the bridge between Django's URL resolver and the view class. It returns a callable function (a closure) that, when executed, instantiates the view and triggers the dispatch process.

# Example from tests/generic_views/test_base.py
class SimpleView(View):
def get(self, request):
return HttpResponse("This is a simple view")

# Usage in URLconf or tests
view_callable = SimpleView.as_view()
response = view_callable(request)

During this phase, as_view() performs sanity checks on the arguments passed to it. It ensures that:

  • No argument matches an HTTP method name (e.g., SimpleView.as_view(get='foo') is forbidden).
  • Every argument corresponds to an existing attribute on the class.

2. Initialization: setup()

Before any logic runs, the setup() method initializes the view instance with the request, positional args, and keyword kwargs captured from the URL pattern.

A critical feature of setup() is its automatic handling of HEAD requests. If a view implements get but not head, setup() automatically aliases self.head to self.get:

def setup(self, request, *args, **kwargs):
if hasattr(self, "get") and not hasattr(self, "head"):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs

3. Routing: dispatch()

The dispatch() method is the engine of the class. It inspects the request.method, converts it to lowercase, and attempts to find a matching method on the instance.

def dispatch(self, request, *args, **kwargs):
method = request.method.lower()
if method in self.http_method_names:
handler = getattr(self, method, self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

If the method is not in the allowed list (http_method_names) or the class does not implement the corresponding handler, it falls back to http_method_not_allowed, which returns an HttpResponseNotAllowed (HTTP 405).

HTTP Method Mapping

The View class maintains a list of supported HTTP verbs in the http_method_names attribute:

  • get, post, put, patch, delete, head, options, trace

To handle a specific verb, you simply define a method with that name. For example, the JavaScriptCatalog in django/views/i18n.py implements a get method to serve translation data:

class JavaScriptCatalog(View):
def get(self, request, *args, **kwargs):
# ... logic to prepare translation ...
context = self.get_context_data(**kwargs)
return self.render_to_response(context)

Asynchronous Dispatching

The View class supports both synchronous and asynchronous handlers. The view_is_async property determines the nature of the view by inspecting the defined handlers.

The All-or-Nothing Rule

A single View class must have handlers that are either all synchronous or all asynchronous. Mixing async def get with def post will raise an ImproperlyConfigured error.

# Example from tests/async/tests.py
class AsyncView(View):
async def get(self, request, *args, **kwargs):
return HttpResponse("Hello (async) world!")

When view_is_async is true, the closure returned by as_view() is marked as a coroutine function using markcoroutinefunction(view), allowing Django's asynchronous request handler to await it correctly.

Common Pitfalls

Overriding setup()

If you override the setup() method to perform custom initialization, you must call super().setup(). Failing to do so will result in an AttributeError because the request attribute will never be attached to the instance.

# Correct way to override setup
class MyView(View):
def setup(self, request, *args, **kwargs):
self.custom_attr = "initialized"
super().setup(request, *args, **kwargs)

Invalid as_view() Arguments

Arguments passed to as_view() are used to override class attributes for that specific instance. However, you cannot pass an argument that isn't already defined on the class.

class CustomizableView(View):
parameter = "default"

# Valid: 'parameter' exists on the class
view = CustomizableView.as_view(parameter="custom")

# Invalid: 'unknown' does not exist, raises TypeError
view = CustomizableView.as_view(unknown="value")

Decorating Dispatch

To apply decorators to a class-based view, the most common approach is to decorate the dispatch() method. The as_view() mechanism is designed to copy attributes (like @csrf_exempt) from the dispatch method onto the returned closure, ensuring decorators work as expected.

class DecoratedDispatchView(View):
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)