~10 min read • Updated Mar 14, 2026
Introduction
Class‑based views (CBVs) in Django provide an alternative way to implement views using Python classes instead of functions. They don’t replace function‑based views, but they offer structural and organizational advantages that make them especially useful in larger or more complex applications.
Why Use Class‑Based Views?
- Cleaner organization of HTTP methods: Instead of branching logic inside a single function, each HTTP method (GET, POST, etc.) gets its own class method.
- Object‑oriented design: CBVs support inheritance and mixins, allowing developers to reuse and extend behavior efficiently.
- Greater flexibility: CBVs provide hooks and override points that make customization easier than with function‑based generic views.
The Evolution of Generic Views
Originally, Django only provided function‑based views: Django passed an HttpRequest to your function and expected an HttpResponse in return.
As common patterns emerged, Django introduced function‑based generic views to simplify repetitive tasks. However, these generic views were limited — they handled simple cases well but were difficult to extend.
To solve this, Django introduced class‑based generic views. These use mixins and inheritance to create a flexible toolkit that can be extended at many levels. Instead of relying on a few configuration options, CBVs expose numerous hooks such as get_form(), get_form_class(), and many others, enabling deep customization.
How Class‑Based Views Work
At their core, CBVs map HTTP methods to class methods. For example, instead of writing conditional logic inside a function:
def my_view(request):
if request.method == "GET":
return HttpResponse("result")
You define a class with a get() method:
from django.views import View
from django.http import HttpResponse
class MyView(View):
def get(self, request):
return HttpResponse("result")
How CBVs Integrate with URLconf
Django’s URL resolver expects a callable, not a class.
To bridge this gap, CBVs provide the as_view() class method:
from django.urls import path
from myapp.views import MyView
urlpatterns = [
path("about/", MyView.as_view()),
]
as_view() creates an instance of the class, runs setup(), and then calls dispatch(), which determines the request method and routes it to the appropriate handler (e.g., get(), post()).
Configuring Class Attributes
CBVs can be customized in two ways:
1. Subclassing and overriding attributes
class GreetingView(View):
greeting = "Good Day"
def get(self, request):
return HttpResponse(self.greeting)
class MorningGreetingView(GreetingView):
greeting = "Morning to ya"
2. Passing attributes to as_view()
urlpatterns = [
path("about/", GreetingView.as_view(greeting="G'day")),
]
Important: Attributes passed through as_view() are set only once when URLs are loaded, not per request.
Conclusion
Class‑based views offer a powerful, flexible, and extensible way to structure views in Django. By separating HTTP methods into dedicated class methods, supporting mixins, and providing numerous override points, CBVs make it easier to build clean, maintainable, and scalable applications. Whether you're creating simple pages or complex, reusable view logic, CBVs provide a strong foundation for modern Django development.
Introduction
Mixins are a powerful feature of Django’s class‑based view (CBV) architecture. They allow developers to reuse behavior across multiple classes through multiple inheritance. When used correctly, mixins help keep code modular and maintainable. However, they must be used carefully to avoid overly complex inheritance chains.
What Are Mixins?
A mixin is a class that provides reusable methods or attributes, intended to be combined with other classes. In Django’s generic CBVs, mixins are used extensively to build flexible and extensible view classes.
For example, TemplateResponseMixin defines the render_to_response() method. When combined with Django’s base View class, the result is TemplateView, which:
- dispatches requests to the correct HTTP method handler (from
View) - renders a template using
template_name(fromTemplateResponseMixin)
Limitations of Mixins
While mixins promote code reuse, they can also make class hierarchies harder to understand. The deeper the inheritance tree, the harder it becomes to determine:
- which method comes from which mixin
- which method should be overridden
- how the final behavior is composed
Another important rule:
You can only inherit from one class that itself inherits from View.
All other parent classes must be mixins.
For example, combining ProcessFormView and ListView directly will not work because both inherit from View.
Handling Forms with Class‑Based Views
A typical function‑based view that handles a form looks like this:
def myview(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
return HttpResponseRedirect("/success/")
else:
form = MyForm(initial={"key": "value"})
return render(request, "form_template.html", {"form": form})
The equivalent class‑based view:
class MyFormView(View):
form_class = MyForm
initial = {"key": "value"}
template_name = "form_template.html"
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
return HttpResponseRedirect("/success/")
return render(request, self.template_name, {"form": form})
This structure makes it easy to customize the view by:
- overriding class attributes like
form_class - overriding methods like
get()orpost() - passing attributes via
as_view()
Decorating Class‑Based Views
CBVs can also be extended using decorators, but the approach differs depending on whether you decorate:
- the result of
as_view()(per‑instance) - the class itself (all instances)
Decorating in URLconf
You can wrap as_view() with a decorator:
urlpatterns = [
path("about/", login_required(TemplateView.as_view(template_name="secret.html"))),
path("vote/", permission_required("polls.can_vote")(VoteView.as_view())),
]
Decorating the Class
To decorate every instance of a CBV, apply the decorator to the dispatch() method using method_decorator:
class ProtectedView(TemplateView):
template_name = "secret.html"
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
Or more concisely:
@method_decorator(login_required, name="dispatch")
class ProtectedView(TemplateView):
template_name = "secret.html"
Using Multiple Decorators
You can apply multiple decorators at once:
decorators = [never_cache, login_required]
@method_decorator(decorators, name="dispatch")
class ProtectedView(TemplateView):
template_name = "secret.html"
Decorators run in the order they are listed.
Conclusion
Mixins are a powerful mechanism for reusing behavior across Django class‑based views, but they must be used thoughtfully to avoid overly complex inheritance structures. CBVs also make form handling cleaner and more modular, and decorators can be applied either per‑instance or at the class level using method_decorator. Together, these tools make Django’s CBV system flexible, extensible, and ideal for building maintainable applications.
Introduction
Mixins are a powerful feature in Django’s class‑based view (CBV) architecture. They allow developers to reuse behavior across multiple classes through multiple inheritance. When used properly, mixins help keep code modular, clean, and maintainable. This article explores how mixins work, how forms are handled in CBVs, and how decorators can be applied to class‑based views.
Using Mixins
A mixin is a class that provides reusable methods or attributes and is intended to be combined with other classes. In Django’s generic CBVs, mixins are used extensively to build flexible and extensible view classes.
For example, TemplateResponseMixin defines the render_to_response() method. When combined with Django’s base View class, it forms TemplateView, which:
- dispatches requests to the correct HTTP method handler (from
View) - renders a template using
template_name(fromTemplateResponseMixin)
Limitations of Mixins
Although mixins promote code reuse, they can also make class hierarchies harder to understand. The deeper the inheritance tree, the harder it becomes to determine:
- which method comes from which mixin
- which method should be overridden
- how the final behavior is composed
Another important rule is that you can only inherit from one class that itself inherits from View.
All other parent classes must be mixins.
For example, combining ProcessFormView and ListView directly will not work because both inherit from View.
Handling Forms with Class‑Based Views
A typical function‑based view for handling forms looks like this:
def myview(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
return HttpResponseRedirect("/success/")
else:
form = MyForm(initial={"key": "value"})
return render(request, "form_template.html", {"form": form})
The equivalent class‑based view is cleaner and more extensible:
class MyFormView(View):
form_class = MyForm
initial = {"key": "value"}
template_name = "form_template.html"
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
return HttpResponseRedirect("/success/")
return render(request, self.template_name, {"form": form})
This structure allows easy customization by:
- overriding class attributes like
form_classortemplate_name - overriding methods such as
get()orpost() - passing new values through
as_view()
Decorating Class‑Based Views
Since CBVs are classes rather than functions, applying decorators works differently. There are two main approaches:
1. Decorating in URLconf
You can wrap the result of as_view() with a decorator:
urlpatterns = [
path("about/", login_required(TemplateView.as_view(template_name="secret.html"))),
path("vote/", permission_required("polls.can_vote")(VoteView.as_view())),
]
2. Decorating the Class
To decorate every instance of a CBV, apply the decorator to the dispatch() method using method_decorator:
class ProtectedView(TemplateView):
template_name = "secret.html"
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
Or more concisely:
@method_decorator(login_required, name="dispatch")
class ProtectedView(TemplateView):
template_name = "secret.html"
Applying Multiple Decorators
You can apply several decorators at once:
decorators = [never_cache, login_required]
@method_decorator(decorators, name="dispatch")
class ProtectedView(TemplateView):
template_name = "secret.html"
Decorators run in the order they are listed.
Conclusion
Mixins provide a powerful mechanism for reusing behavior across Django class‑based views, but they must be used thoughtfully to avoid overly complex inheritance structures. CBVs also make form handling cleaner and more modular, and decorators can be applied either per‑instance or at the class level using method_decorator. Together, these tools make Django’s CBV system flexible, extensible, and ideal for building maintainable applications.
Written & researched by Dr. Shahin Siami