Difficulty: Beginner
Class-based views (CBVs) provide an alternative to function-based views by representing views as Python classes instead of functions. The key advantage of CBVs is reusability through inheritance and mixins. Instead of duplicating common patterns across multiple function views, you can define behavior once in a parent class and inherit it across many views.
The base class for all CBVs is django.views.View. It provides a dispatch() method that routes the incoming request to the appropriate handler method (get, post, put, delete, etc.) based on the HTTP method. You define handler methods with lowercase names matching HTTP methods. The as_view() class method converts the class into a callable view function that can be used in URL patterns.
When a request arrives, Django calls as_view(), which creates an instance of the view class and calls its dispatch() method. dispatch() inspects request.method and calls self.get() for GET requests, self.post() for POST requests, and so on. If no handler exists for the HTTP method, Django returns a 405 Method Not Allowed response.
CBVs shine when combined with mixins. A mixin is a class that provides specific functionality meant to be used with multiple inheritance. Django provides mixins like LoginRequiredMixin, PermissionRequiredMixin, and UserPassesTestMixin. Third-party packages add more. Mixins are listed before the main view class in the inheritance order (Python's Method Resolution Order, or MRO, processes them left to right).
The TemplateView is a simple CBV that renders a template. ListView and DetailView handle listing multiple objects and displaying a single object, respectively. CreateView, UpdateView, and DeleteView handle CRUD operations with forms. These generic views handle the boilerplate for you - fetching data, rendering templates, processing forms - so you only need to specify the model, template name, and any customizations.
CBVs can access the same request attributes as FBVs through self.request. The URL parameters are available via self.kwargs (keyword arguments from the URL pattern) and self.args (positional arguments). You can override methods like get_queryset(), get_context_data(), and form_valid() to customize behavior.
The debate between FBVs and CBVs is ongoing in the Django community. FBVs are simpler, more explicit, and easier to understand. CBVs reduce code duplication and enable DRY patterns through inheritance. In practice, many Django developers use FBVs for simple or unique views and CBVs (especially generic views) for standard CRUD operations.
from django.views import View
from django.http import HttpResponse
from django.shortcuts import render
class HelloView(View):
def get(self, request):
return HttpResponse('Hello from GET!')
def post(self, request):
return HttpResponse('Hello from POST!')
# urls.py
from django.urls import path
from .views import HelloView
urlpatterns = [
path('hello/', HelloView.as_view(), name='hello'),
]
CBVs define separate methods for each HTTP verb. The dispatch() method routes requests to the correct handler. as_view() converts the class into a callable for URL patterns.
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from .models import Article
from .forms import ArticleForm
class ArticleCreateView(View):
template_name = 'blog/article_form.html'
def get(self, request):
form = ArticleForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
return redirect('blog:article-detail', pk=article.pk)
return render(request, self.template_name, {'form': form})
This CBV separates GET (display form) and POST (process form) into clean, distinct methods. Class attributes like template_name can be set at the class level and overridden per-instance.
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views import View
from django.shortcuts import render, get_object_or_404
from .models import Article
class ArticleEditView(LoginRequiredMixin, UserPassesTestMixin, View):
login_url = '/accounts/login/'
raise_exception = False
def test_func(self):
article = get_object_or_404(Article, pk=self.kwargs['pk'])
return article.author == self.request.user
def get(self, request, pk):
article = get_object_or_404(Article, pk=pk)
return render(request, 'blog/article_edit.html', {
'article': article,
})
# Mixin order matters! Mixins go BEFORE the base view class.
# LoginRequiredMixin -> UserPassesTestMixin -> View
Mixins add functionality through multiple inheritance. LoginRequiredMixin checks authentication. UserPassesTestMixin runs a custom test via test_func(). Mixins must be listed BEFORE the base View class in the class definition.
# Function-Based View
from django.contrib.auth.decorators import login_required
@login_required
def article_list(request):
articles = Article.objects.filter(author=request.user)
return render(request, 'blog/article_list.html', {
'articles': articles,
})
# Equivalent Class-Based View
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
class ArticleListView(LoginRequiredMixin, View):
def get(self, request):
articles = Article.objects.filter(author=request.user)
return render(request, 'blog/article_list.html', {
'articles': articles,
})
# URLs
urlpatterns = [
path('articles/', article_list, name='article-list'),
# OR
path('articles/', ArticleListView.as_view(), name='article-list'),
]
Both achieve the same result. FBV uses a decorator for authentication; CBV uses a mixin. For simple views, FBVs are often more readable. For views with shared behavior, CBVs enable code reuse through inheritance.
CBV, View, as_view, Method Dispatch, Mixins