类视图(CBV)重构
本章你将学习 Django 的类视图(CBV),用更简洁的代码重构首页和详情页。
FBV vs CBV
到目前为止,我们所有的视图都是用函数写的(Function-Based View,FBV)。
Django 还提供了 类视图(Class-Based View,CBV),用类来封装视图逻辑。
| 特性 | FBV(函数视图) | CBV(类视图) |
|---|---|---|
| 代码量 | 灵活,但常用逻辑需要重复写 | 继承通用视图后只需几行代码 |
| 可读性 | 线性流程,逻辑清晰 | 继承链需要追踪,但熟悉后一目了然 |
| 复用性 | 通过函数封装 | 通过 Mixin 组合(Django 的特色) |
| 适用场景 | 逻辑简单、流程清晰的页面 | 增删改查这类模式化的页面 |
| HTTP 方法分发 | 手动 if request.method == 'POST' | 自动分发到 get() / post() 方法 |
两者没有绝对的优劣,Django 项目通常是 FBV 和 CBV 混用:复杂逻辑用 FBV,标准 CRUD 用 CBV。
ListView — 列表页重构
首页的 index 视图本质是:查询文章列表 → 渲染模板。
这类模式化的逻辑,Django 的 ListView 已经帮你写好了。
实例
from django.views.generic import ListView
from django.db.models import Q
from .models import Post, Category
class PostListView(ListView):
"""博客首页:继承 ListView,展现文章列表"""
model = Post # 指定模型
template_name = 'blog/index.html' # 指定模板(默认:blog/post_list.html)
context_object_name = 'posts' # 模板中使用的变量名(默认:object_list)
paginate_by = 12 # 每页 12 条(分页)
ordering = ['-created_at'] # 排序
def get_queryset(self):
"""重写查询方法:支持分类筛选 + 关键词搜索"""
queryset = super().get_queryset()
# 分类筛选
self.category_slug = self.request.GET.get('category', '')
if self.category_slug:
queryset = queryset.filter(category__slug=self.category_slug)
# 关键词搜索
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):
"""重写上下文方法:往模板中注入额外数据"""
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['category_slug'] = self.category_slug
context['keyword'] = self.keyword
context['title'] = 'RUNOOB 博客 - 首页'
return context
对比一下重构前后的代码量:FBV 约 40 行 → CBV 约 30 行,而且逻辑被清晰地划分到不同方法中。
DetailView — 详情页重构
实例
from django.views.generic import DetailView
class PostDetailView(DetailView):
"""文章详情页:继承 DetailView"""
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
# pk_url_kwarg:URL 中传递主键的参数名(默认是 pk,这里显式声明一下)
pk_url_kwarg = 'pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'{self.object.title} - RUNOOB 博客'
return context
更新路由配置
CBV 的路由写法与 FBV 略有不同——需要调用 .as_view()。
实例
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
# CBV:类后面加 .as_view() 将其转为视图函数
path('', views.PostListView.as_view(), name='index'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
# 其他视图不变
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'),
]
为什么 CBV 要用
.as_view()?Django 的 URL 配置期望的是一个可调用对象(函数)。.as_view()将类视图转换成一个符合 Django 视图函数签名的可调用对象,内部负责根据 HTTP 方法(GET/POST)自动分发到类的 get() 或 post() 方法。
常用通用视图一览
| 视图 | 模板默认名 | 用途 |
|---|---|---|
| ListView | 模型名_list.html | 展示对象列表 |
| DetailView | 模型名_detail.html | 展示单条对象详情 |
| CreateView | 模型名_form.html | 创建新对象(含表单) |
| UpdateView | 模型名_form.html | 更新现有对象 |
| DeleteView | 模型名_confirm_delete.html | 确认删除对象 |
| TemplateView | 需指定 | 渲染静态模板(无数据查询) |
Django 的通用视图覆盖了约 80% 的 Web 开发场景。遇到不满足需求的情况再退回 FBV。
LoginRequiredMixin
CBV 不能用 @login_required 装饰器(那是给函数用的),改用 LoginRequiredMixin。
实例
from django.views.generic import ListView
class FavoriteListView(LoginRequiredMixin, ListView):
"""收藏列表:需要登录才能访问"""
model = Post
template_name = 'blog/favorites.html'
context_object_name = 'posts'
ordering = ['-created_at']
# LoginRequiredMixin 未登录用户会自动跳转到 settings.LOGIN_URL
def get_queryset(self):
# 只显示当前用户收藏的文章
return self.request.user.favorite_posts.all().order_by('-created_at')
Mixin 的继承顺序很重要:
LoginRequiredMixin必须写在ListView前面。Python 多继承的解析顺序是从左到右,LoginRequiredMixin 先检查登录状态,通过后才继续执行 ListView 的逻辑。
什么时候用 CBV,什么时候用 FBV?
经验法则:
- FBV:逻辑复杂、条件分支多、非标准流程的视图(如 toggle_favorite)
- CBV:标准 CRUD 操作、逻辑可以清晰拆分为 get/post 的视图(如列表页、详情页)
toggle_favorite(收藏切换)这类操作逻辑简单但流程不同于常规 CRUD,FBV 更合适。
index 和 post_detail 这类标准的数据展示页面,CBV 可以显著减少样板代码。
本章小结
本章你学会了 Django 类视图的核心用法:ListView 展示列表、DetailView 展示详情、get_queryset/get_context_data 定制查询和上下文、as_view() 在路由中启用、LoginRequiredMixin 替代 login_required。
重构后的首页和详情页代码更短、结构更清晰。
