Class-Based View (CBV) Refactoring
In this chapter, you will learn Django's class-based views (CBV) and refactor the homepage and detail page with more concise code.
FBV vs CBV
Up to now, all our views have been written using functions (Function-Based Views, FBV).
Django also providesClass-Based View (CBV), using classes to encapsulate view logic.
| Features | FBV (Function-Based View) | CBV (Class-Based Views) |
|---|---|---|
| code volume | Flexible, but common logic needs to be written repeatedly | After inheriting the generic view, only a few lines of code are needed |
| Readability | Linear flow, clear logic | The inheritance chain requires tracking, but once familiar, it's clear at a glance. |
| Reusability | Encapsulated via functions | Using Mixin composition (a Django hallmark) |
| Applicable scenarios | Pages with simple logic and clear flow | Patterned pages such as create, read, update, delete (CRUD) |
| HTTP method dispatch | Manual if request.method == 'POST' | Automatically dispatch to get() / post() methods |
There is no absolute advantage or disadvantage between the two; Django projects usually...Mixing FBV and CBV: Use FBV for complex logic, and CBV for standard CRUD.
ListView — refactoring the list page
The index view of the homepage is essentially:Query article list → render template。
For this kind of patterned logic, Django's...ListViewAlready written for you.
Example
from django.views.generic import ListView
from django.db.models import Q
from .models import Post, Category
class PostListView(ListView):
"""Blog homepage: inherits ListView, displays article list"""
model = Post # Specify model
template_name = 'blog/index.html' # Specify the template (default: blog/post_list.html)
context_object_name = 'posts' # The variable name used in the template (default: object_list)
paginate_by = 12 # 12 items per page (pagination)
ordering = ['-created_at'] # Sort
def get_queryset(self):
"""Override the query method: supports category filtering + keyword search"""
queryset = super().get_queryset()
# Category filter
self.category_slug = self.request.GET.get('category', '')
if self.category_slug:
queryset = queryset.filter(category__slug=self.category_slug)
# Keyword search
self.keyword = self.request.GET.get('q', '')
if self.keyword:
queryset = queryset.filter(
Q(title__icontains=self.keyword) |
Q(summary__icontains=self.keyword)
)
return queryset
def get_context_data(self, **kwargs):
"""Override context method: inject additional data into the template"""
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['category_slug'] = self.category_slug
context['keyword'] = self.keyword
context['title'] = 'EXAMPLE Blog - Home'
return context
Compare the code volume before and after refactoring: FBV is about 40 lines → CBV is about 30 lines, and the logic is clearly divided into different methods.
DetailView — refactoring the detail page
Example
from django.views.generic import DetailView
class PostDetailView(DetailView):
"""Article detail page: inherits DetailView"""
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
# pk_url_kwarg: parameter name for passing the primary key in the URL (default is pk; explicitly declared here).
pk_url_kwarg = 'pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'{self.object.title} - EXAMPLE Blog'
return context
Update route configuration
CBV routing syntax is slightly different from FBV — it requires calling....as_view()。
Example
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
# CBV: add .as_view() after the class to convert it into a view function
path('', views.PostListView.as_view(), name='index'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
# Other views remain unchanged
path('register/', views.register, name='register'),
path('login/', auth_views.LoginView.as_view(
template_name='blog/login.html',
redirect_authenticated_user=True
), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('post/<int:pk>/favorite/', views.toggle_favorite, name='toggle_favorite'),
]
Why use CBV?
.as_view()? Django's URL configuration expects a callable object (function)..as_view()It converts the class view into a callable object that conforms to the Django view function signature, and internally handles automatically dispatching to the class's get() or post() method based on the HTTP method (GET/POST).
Overview of common generic views
| view | Default template name | Purpose |
|---|---|---|
| ListView | modelname_list.html | Display object list |
| DetailView | modelname_detail.html | Display details of a single object |
| CreateView | modelname_form.html | Create a new object (with form) |
| UpdateView | modelname_form.html | Update existing object |
| DeleteView | model_name_confirm_delete.html | Confirm object deletion |
| TemplateView | need to specify | Render a static template (no data query) |
Django's generic views cover about 80% of web development scenarios. When encountering situations that don't meet your needs, fall back to FBV.
LoginRequiredMixin
CBV not available@login_requiredDecorators (those are for functions), switch to...LoginRequiredMixin。
Example
from django.views.generic import ListView
class FavoriteListView(LoginRequiredMixin, ListView):
"""Favorites list: login required to access"""
model = Post
template_name = 'blog/favorites.html'
context_object_name = 'posts'
ordering = ['-created_at']
# LoginRequiredMixin: unauthenticated users will be automatically redirected to settings.LOGIN_URL
def get_queryset(self):
# Only display articles favorited by the current user
return self.request.user.favorite_posts.all().order_by('-created_at')
The inheritance order of Mixins is very important:
LoginRequiredMixinMust Be Written InListViewEarlier. Python's multiple inheritance resolution order is from left to right; LoginRequiredMixin first checks the login status, and only after passing does it continue executing the ListView logic.
When to use CBV, and when to use FBV?
Rule of thumb:
- FBV: Views with complex logic, many conditional branches, and non-standard flows (such as toggle_favorite)
- CBV: Views with standard CRUD operations and logic that can be clearly split into get/post (such as list pages, detail pages)
Operations like toggle_favorite (favorite toggle) have simple logic but a flow different from conventional CRUD; FBV is more appropriate.
For standard data display pages like index and post_detail, CBV can significantly reduce boilerplate code.
Chapter summary
In this chapter, you learned the core usage of Django class-based views: ListView displays lists, DetailView displays details, get_queryset/get_context_data customize queries and context, as_view() enables routing, and LoginRequiredMixin replaces login_required.
The refactored homepage and detail page code is shorter and has a clearer structure.
other extensions