Django Intermediate

Django Class-Based Views: Complete Guide

Understand Django class-based views (CBVs). Covers ListView, DetailView, CreateView, UpdateView, mixins, and when to use CBVs vs FBVs.

DjangoZen Team Mar 29, 2026 4 min read 27 views

Class-Based Views (CBVs) let you build views using Python classes instead of functions. They provide reusable patterns for common tasks like displaying lists, forms, and detail pages. This guide covers the most important CBVs and when to use them.

Function vs Class-Based Views

# Function-Based View (FBV)
from django.shortcuts import render, get_object_or_404
from .models import Product

def product_list(request):
    products = Product.objects.filter(is_active=True)
    return render(request, 'products/list.html', {'products': products})

# Same thing as Class-Based View (CBV)
from django.views.generic import ListView

class ProductListView(ListView):
    model = Product
    template_name = 'products/list.html'
    context_object_name = 'products'
    queryset = Product.objects.filter(is_active=True)

The Most Common CBVs

ListView — Display a List

from django.views.generic import ListView

class ProductListView(ListView):
    model = Product
    template_name = 'products/list.html'
    context_object_name = 'products'
    paginate_by = 12
    ordering = ['-created_at']

    def get_queryset(self):
        qs = super().get_queryset().filter(is_active=True)
        category = self.request.GET.get('category')
        if category:
            qs = qs.filter(category__slug=category)
        return qs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['categories'] = Category.objects.all()
        return context

DetailView — Display a Single Object

from django.views.generic import DetailView

class ProductDetailView(DetailView):
    model = Product
    template_name = 'products/detail.html'
    context_object_name = 'product'
    slug_field = 'slug'          # model field
    slug_url_kwarg = 'slug'     # URL parameter name

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['related'] = Product.objects.filter(
            category=self.object.category
        ).exclude(pk=self.object.pk)[:4]
        return context

CreateView — Create an Object

from django.views.generic import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy

class ProductCreateView(LoginRequiredMixin, CreateView):
    model = Product
    fields = ['title', 'description', 'price', 'category']
    template_name = 'products/create.html'
    success_url = reverse_lazy('product_list')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

UpdateView — Edit an Object

from django.views.generic import UpdateView

class ProductUpdateView(LoginRequiredMixin, UpdateView):
    model = Product
    fields = ['title', 'description', 'price']
    template_name = 'products/edit.html'

    def get_success_url(self):
        return self.object.get_absolute_url()

DeleteView — Delete an Object

from django.views.generic import DeleteView

class ProductDeleteView(LoginRequiredMixin, DeleteView):
    model = Product
    template_name = 'products/confirm_delete.html'
    success_url = reverse_lazy('product_list')

URL Configuration

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.ProductListView.as_view(), name='product_list'),
    path('<slug:slug>/', views.ProductDetailView.as_view(), name='product_detail'),
    path('create/', views.ProductCreateView.as_view(), name='product_create'),
    path('<slug:slug>/edit/', views.ProductUpdateView.as_view(), name='product_edit'),
    path('<slug:slug>/delete/', views.ProductDeleteView.as_view(), name='product_delete'),
]
Note: CBVs use .as_view() in URLs. This converts the class into a callable view function that Django's URL dispatcher expects.

Useful Mixins

from django.contrib.auth.mixins import (
    LoginRequiredMixin,      # Require login
    PermissionRequiredMixin, # Require specific permission
    UserPassesTestMixin,     # Custom permission logic
)

# Only the author can edit their product
class ProductUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Product
    fields = ['title', 'price']

    def test_func(self):
        product = self.get_object()
        return self.request.user == product.author

FormView — Custom Forms

from django.views.generic import FormView
from .forms import ContactForm

class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = reverse_lazy('contact_thanks')

    def form_valid(self, form):
        form.send_email()
        return super().form_valid(form)

When to Use FBV vs CBV

Use FBV WhenUse CBV When
Simple, one-off viewsStandard CRUD operations
Complex conditional logicReusable patterns needed
Quick prototypingConsistent structure across views
You find CBVs confusingYou need built-in pagination/forms
Pro Tip: There's no rule saying you must use one or the other. Many Django projects mix both. Use CBVs for standard CRUD and FBVs for custom logic. The best view is the one you can understand and maintain.

Key Methods to Override

MethodPurposeCommon In
get_queryset()Filter which objects to showListView
get_context_data()Add extra data to templateAll views
form_valid()Custom logic after form validationCreateView, UpdateView
get_success_url()Where to redirect after successCreate, Update, Delete
get_object()Customize how single object is fetchedDetailView, UpdateView
dispatch()Run logic before any HTTP methodAll views

Summary

  • ListView for listing objects with pagination and filtering
  • DetailView for showing a single object
  • CreateView/UpdateView for form handling with automatic model saving
  • DeleteView for confirmation and deletion
  • Use mixins for authentication and permissions
  • Override get_queryset() and get_context_data() for customization
  • Mix FBVs and CBVs freely — use what makes sense for each view