Django Unleashed

Unleashing the Full Potential of Web Development

Mastering Django CBVs: The Hidden Power of setup() and dispatch()

Adrien Van Thong
Django Unleashed
Published in
5 min readFeb 10, 2025

--

Django CBVs provide an immense amount of flexibility and built-in functionality right out the gate. Two often-overlooked methods provided by CBVs are the setup() and dispatch() methods — both of which are incredibly important to the CBV workflow, and when overwritten properly can help take your CBVs to the next level.

What are the setup() and dispatch() methods?

Both the setup() and dispatch() methods play a crucial role in the base CBV workflow. First, let’s properly define what each of these two methods are responsible for:

  • setup() is the first method invoked after initialization, and is specifically meant to set local attributes. It is only ever defined once in the top-level View class to set these three attributes:self.request, self.args, and self.kwargs.
  • dispatch() is invoked next, and its role is to determine and route the CBV to the correct method depending on the HTTP verb being used. For example, the HTTP GET request executes the get() method.

With that quick overview out of the way, let’s dive into these further, and come up with some useful examples of when to overwrite these methods.

Overwriting setup()

As noted earlier in this article, setup() is the first method that runs after the View is instantiated, which means by the time this method is done, it will have access to the request object. This makes it a great place to set some local attributes that will be relied upon in the other methods in the CBV.

It’s really important to note that when overwriting this method, the super() method must always be invoked as the rest of the view will not function properly without the attributes which it sets.

One very useful reason to overwrite this method is to extract user data from the request object for convenience to avoid having to repeat the same code in multiple other methods. i.e. pulling out specific user profile settings or client request attributes.

Here is a code sample where we store the current user’s favourite color, so we can re-use it in other methods:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from .models import Candle

class FavCandleListView(LoginRequiredMixin, ListView):
"""
Show all Candle objects matching the current user's favourite color
"""
model = Candle

def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.fav_color = request.user.profile.fav_color

def get_queryset(self):
return super().get_queryset().filter(color=self.fav_color)

def get_context_data(self, **kwargs):
context = super().get_context_data(kwargs)
context['fav_color'] = self.fav_color
return context

Although the amount of code reuse saved here is minimal, this example serves to illustrate how the setup method can be leveraged to avoid having to repeat the same code across multiple methods in your CBVs.

Overwriting dispatch()

The dispatch() method runs immediately after the setup() method and is responsible for routing the request to the correct method based on the HTTP verb used. For example, dispatch() will detect the current HTTP POST request to update a record and invoke the post() method.

It’s generally quite rare to overwrite the dispatch() method, as its base functionality generally does everything we need it to, though on occasion it can come in handy. One simple example is adding an extra custom header to the HTTP response:

from django.views.generic import UpdateView

class CandleUpdateView(UpdateView):
model = Candle

def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
response["X-CandlesApp"] = "Special Candles App header value"
return response

The benefit of this approach is that the extra header is automatically added for both GET and POST responses since both are handled by the same CBV.

What about __init__()?

Some of the local attribute instantiation could easily happen inside the CBV’s __init__() method instead of the setup() method. Since the setup() method runs immediately after the __init__() method, the difference comes down to a single differentiator: by the time the setup() method runs, the request variable is already available. Whether you put your instantiation in the setup() or __init__() method will come down to any dependency on the request object. Outside of that dependency, it’s purely a matter of personal preference.

Putting it all together

Let’s try this using a real-time example: given a Django app that needs multiple-level user authorization access levels (beyond what is provided by default by Django) for a single view. In this particular example, we have a user base which falls into 5 different access level types, and with each incrementing security level, the user has access to more records of a particular model.

Let’s try implementing that by overwriting the CBV methods:

from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Document
from .utils import convert_mask_to_level, log_elevated_access

class RestrictedDocumentListView(LoginRequiredMixin, ListView):
model = Document

def setup(self, request, *args, **kwargs):
"""
Convert the user's profile's permission mask into a single integer by utilizing
the convert method which does some complex operations.
"""
super().setup(request, *args, **kwargs)
self.security_level: int = convert_mask_to_level(self.request.user.profile.permission_mask)

def dispatch(self, request, *args, **kwargs):
"""
Minimum security level to view this page is 2.
"""
if self.security_level <= 1:
return HttpResponseForbidden("Access Denied")
if self.security_level == 5:
log_elevated_access(request)
return super().dispatch(request, *args, **kwargs)

def get_template_names(self):
"""
Lower security levels will render a template showing fewer fields.
"""
if self.security_level == 2:
return ['limited_documents_list.html']
elif self.security_level < 5:
return ['documents_list.html']
elif self.security_level == 5:
return ['full_access_documents_list.html']

def get_queryset(self):
"""
Limit records to the user's security level or lower
"""
return super().get_queryset().filter(min_level__lt=self.security_level)

def get_context_data(self, **kwargs):
context = super().get_context_data(kwargs)
context['security_level'] = self.security_level
return context

In our CBV above, we first overwrite the setup() method to calculate the user’s “security level” based off their profile’s permission mask using our made-up conversion method. This is the variable we’ll keep reading in all our other overwritten methods.

Next, the dispatch() method has been overwritten to return an HTTP forbidden for users with too low a security level, and to log the access request when a user with an elevated security level accesses the page. Afterwards, the request is processed as normal by invoking the super()'s dispatch method.

The get_templates() is overwritten to render the page using a different template depending on the current user’s security level. For example, the page may look completely different (i.e. fewer fields/columns) for users with lower privileges.

Likewise, get_queryset() method is overwritten to filter for only records matching the user’s current security level and below.

Finally, we add the security_level attribute to the context in the get_context_data method so we can utilize it directly in the templates.

In conclusion, the humble setup and dispatch methods are another powerful tool that Django CBVs make available at our disposal to easily extend our app’s functionality. The setup() method runs first and is meant to set local attributes that will be read from other methods in the CBV. The dispatch() method runs after the setup() method and is responsible for routing the control flow to the correct method (i.e. get(), post(), etc) based on the HTTP request verb. Overwrite this method to change the control flow or add other custom logic that will effect all HTTP verbs.

Happy coding!

External Resources

--

--

Django Unleashed
Django Unleashed

Published in Django Unleashed

Unleashing the Full Potential of Web Development

Responses (1)