Understand Django class-based views (CBVs). Covers ListView, DetailView, CreateView, UpdateView, mixins, and when to use CBVs vs FBVs.
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-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)
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
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
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)
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()
from django.views.generic import DeleteView
class ProductDeleteView(LoginRequiredMixin, DeleteView):
model = Product
template_name = 'products/confirm_delete.html'
success_url = reverse_lazy('product_list')
# 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'),
]
.as_view() in URLs. This converts the class into a callable view function that Django's URL dispatcher expects.
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
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)
| Use FBV When | Use CBV When |
|---|---|
| Simple, one-off views | Standard CRUD operations |
| Complex conditional logic | Reusable patterns needed |
| Quick prototyping | Consistent structure across views |
| You find CBVs confusing | You need built-in pagination/forms |
| Method | Purpose | Common In |
|---|---|---|
get_queryset() | Filter which objects to show | ListView |
get_context_data() | Add extra data to template | All views |
form_valid() | Custom logic after form validation | CreateView, UpdateView |
get_success_url() | Where to redirect after success | Create, Update, Delete |
get_object() | Customize how single object is fetched | DetailView, UpdateView |
dispatch() | Run logic before any HTTP method | All views |
get_queryset() and get_context_data() for customization