Django类视图CBV完全指南从入门到实战
目录
Django类视图(CBV)完全指南:从入门到实战
Django 各种View类需要实现的方法详解
我将详细解释每种Django类视图的核心方法、执行流程和需要重写的方法。
- View类(基类)
核心方法执行流程
from django.views.generic import View
class MyView(View):
def dispatch(self, request, *args, **kwargs):
"""
请求分发方法 - 必须理解但通常不需要重写
执行顺序:dispatch() → get()/post()等 → 返回HttpResponse
"""
# 1. 预处理请求
# 2. 根据请求方法调用对应的处理方法
# 3. 返回响应
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
"""处理GET请求 - 必须实现"""
return HttpResponse("GET请求处理")
def post(self, request, *args, **kwargs):
"""处理POST请求 - 按需实现"""
return HttpResponse("POST请求处理")
def http_method_not_allowed(self, request, *args, **kwargs):
"""处理不支持的HTTP方法"""
return HttpResponse("方法不允许", status=405)
- TemplateView - 显示模板
核心方法
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'home.html' # 必须设置:模板路径
def get_context_data(self, **kwargs):
"""
最重要的方法 - 几乎总是需要重写
作用:向模板传递额外的上下文数据
"""
context = super().get_context_data(**kwargs) # 必须调用父类方法
context['current_time'] = timezone.now() # 添加自定义数据
context['featured_posts'] = Post.objects.filter(is_featured=True)[:5]
return context
def get(self, request, *args, **kwargs):
"""
可选重写:如果需要复杂的GET请求处理逻辑
通常只需要重写get_context_data()就够了
"""
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
完整示例
class AboutView(TemplateView):
template_name = 'about.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 添加团队信息
context['team_members'] = [
{'name': '张三', 'role': '创始人'},
{'name': '李四', 'role': '技术总监'},
]
# 添加公司统计信息
context['stats'] = {
'projects': 150,
'clients': 89,
'years': 5
}
return context
- ListView - 显示对象列表
必须设置/重写的方法
from django.views.generic import ListView
class PostListView(ListView):
# 必须设置的属性
model = Post # 指定模型类
template_name = 'post_list.html' # 模板路径
context_object_name = 'posts' # 模板中使用的变量名(可选)
paginate_by = 10 # 分页大小(可选)
def get_queryset(self):
"""
最重要的方法 - 控制显示哪些数据
默认:返回模型的所有对象 Model.objects.all()
"""
# 基础查询:只显示已发布的文章
queryset = Post.objects.filter(status='published')
# 搜索功能
search_query = self.request.GET.get('q')
if search_query:
queryset = queryset.filter(title__icontains=search_query)
# 分类过滤
category_id = self.kwargs.get('category_id')
if category_id:
queryset = queryset.filter(category_id=category_id)
return queryset
def get_context_data(self, **kwargs):
"""
添加额外的上下文数据
"""
context = super().get_context_data(**kwargs)
# 添加分类列表
context['categories'] = Category.objects.all()
# 添加搜索词
context['search_query'] = self.request.GET.get('q', '')
# 分页相关信息(自动添加的)
# context['page_obj'] - 当前页对象
# context['paginator'] - 分页器对象
# context['is_paginated'] - 是否分页
return context
分页相关方法
class PaginatedPostListView(ListView):
model = Post
paginate_by = 5
template_name = 'post_list.html'
def get_paginate_by(self, queryset):
"""
动态控制分页大小
可以根据不同条件返回不同的分页大小
"""
if self.request.GET.get('show_all'):
return None # 不分页
return self.paginate_by
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 添加分页信息
page_obj = context['page_obj']
context['page_range'] = range(1, page_obj.paginator.num_pages + 1)
return context
- DetailView - 显示单个对象详情
核心方法
from django.views.generic import DetailView
class PostDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
context_object_name = 'post' # 默认是'object'
def get_queryset(self):
"""
控制哪些对象可以被查看
比get_object()更常用
"""
queryset = super().get_queryset()
# 未登录用户只能看已发布文章
if not self.request.user.is_authenticated:
queryset = queryset.filter(status='published')
# 登录用户可以看到自己的草稿
else:
queryset = queryset.filter(
Q(status='published') |
Q(author=self.request.user)
)
return queryset
def get_object(self, queryset=None):
"""
获取要显示的对象
通常不需要重写,除非有特殊需求
"""
if queryset is None:
queryset = self.get_queryset()
# 从URL参数中获取主键或slug
pk = self.kwargs.get('pk')
slug = self.kwargs.get('slug')
if pk:
return get_object_or_404(queryset, pk=pk)
elif slug:
return get_object_or_404(queryset, slug=slug)
else:
raise AttributeError("必须提供pk或slug")
def get_context_data(self, **kwargs):
"""
添加相关数据
"""
context = super().get_context_data(**kwargs)
post = self.object # 当前显示的文章
# 添加相关文章
context['related_posts'] = Post.objects.filter(
category=post.category
).exclude(id=post.id)[:5]
# 添加评论
context['comments'] = post.comments.filter(active=True)
return context
- CreateView - 创建对象
必须实现的方法
from django.views.generic import CreateView
from django.urls import reverse_lazy
class PostCreateView(CreateView):
model = Post
form_class = PostForm # 指定自定义表单类
template_name = 'post_form.html'
def form_valid(self, form):
"""
表单验证成功时调用 - 必须重写!
在这里可以设置表单实例的额外属性
"""
# 设置作者为当前用户
form.instance.author = self.request.user
# 设置发布时间
form.instance.publish_date = timezone.now()
# 必须调用父类方法保存对象
return super().form_valid(form)
def form_invalid(self, form):
"""
表单验证失败时调用 - 可选重写
可以添加错误处理逻辑
"""
# 记录错误日志
logger.warning(f"表单验证失败: {form.errors}")
return super().form_invalid(form)
def get_success_url(self):
"""
成功创建后重定向的URL - 必须实现
方式1:设置success_url属性
方式2:重写此方法(更灵活)
"""
# 重定向到新创建对象的详情页
return reverse_lazy('post_detail', kwargs={'pk': self.object.pk})
def get_form_kwargs(self):
"""
传递给表单的关键字参数 - 可选重写
"""
kwargs = super().get_form_kwargs()
# 添加当前用户到表单参数
kwargs['user'] = self.request.user
return kwargs
def get_initial(self):
"""
设置表单初始值 - 可选重写
"""
initial = super().get_initial()
initial['author'] = self.request.user
initial['publish_date'] = timezone.now()
return initial
- UpdateView - 更新对象
核心方法(与CreateView类似但有区别)
class PostUpdateView(UpdateView):
model = Post
form_class = PostForm
template_name = 'post_form.html'
def get_object(self, queryset=None):
"""
获取要更新的对象 - 通常需要重写进行权限检查
"""
obj = super().get_object(queryset)
# 检查权限:只有作者可以编辑
if obj.author != self.request.user:
raise PermissionDenied("你没有权限编辑此文章")
return obj
def form_valid(self, form):
"""
表单验证成功时调用
"""
# 自动更新修改时间
form.instance.updated_date = timezone.now()
# 添加成功消息
messages.success(self.request, "文章更新成功!")
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('post_detail', kwargs={'pk': self.object.pk})
- DeleteView - 删除对象
核心方法
class PostDeleteView(DeleteView):
model = Post
template_name = 'post_confirm_delete.html'
success_url = reverse_lazy('post_list') # 删除成功后重定向
def get_object(self, queryset=None):
"""权限检查"""
obj = super().get_object(queryset)
if not (obj.author == self.request.user or self.request.user.is_superuser):
raise PermissionDenied("没有删除权限")
return obj
def delete(self, request, *args, **kwargs):
"""
重写delete方法以添加额外逻辑
"""
# 获取要删除的对象
self.object = self.get_object()
# 记录删除日志
logger.info(f"用户 {request.user} 删除了文章: {self.object.title}")
# 添加成功消息
messages.success(request, "文章删除成功")
# 调用父类方法执行删除
return super().delete(request, *args, **kwargs)
def get_success_url(self):
"""删除成功后的重定向"""
return self.success_url
- FormView - 处理表单(不关联模型)
核心方法
from django.views.generic import FormView
class ContactView(FormView):
template_name = 'contact.html'
form_class = ContactForm # 普通表单,不是ModelForm
success_url = reverse_lazy('contact_success')
def form_valid(self, form):
"""
表单验证成功后的处理
"""
# 发送邮件
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
from_email = form.cleaned_data['email']
send_mail(subject, message, from_email, ['admin@example.com'])
# 添加成功消息
messages.success(self.request, "消息发送成功!")
return super().form_valid(form)
def form_invalid(self, form):
"""表单验证失败"""
messages.error(self.request, "请检查表单错误")
return super().form_invalid(form)
- 各种View的执行流程图
ListView执行流程:
请求进入 → dispatch() → get() → get_queryset() → get_context_data() → render_to_response()
CreateView执行流程:
POST请求 → dispatch() → post() → get_form() → form_valid()/form_invalid()
GET请求 → dispatch() → get() → get_form() → get_context_data() → render_to_response()
方法调用顺序总结:
方法名 调用时机 必须重写? 常见用途
dispatch() 所有请求最先调用 很少 权限检查、请求预处理
get()/post() 根据HTTP方法调用 有时 特殊请求处理
get_queryset() 获取数据时 经常 数据过滤、权限控制
get_object() 获取单个对象时 有时 对象级权限检查
get_context_data() 准备模板数据时 经常 添加额外模板变量
form_valid() 表单验证成功时 经常 保存前处理、消息提示
form_invalid() 表单验证失败时 有时 错误处理
get_success_url() 操作成功后 经常 重定向目标
- 实际应用示例
class AdvancedPostView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
"""综合使用各种方法的示例"""
def test_func(self):
"""UserPassesTestMixin要求的方法"""
return self.request.user.has_perm('blog.add_post')
def get_initial(self):
"""设置初始值"""
return {'category': self.get_default_category()}
def get_form_kwargs(self):
"""传递额外参数给表单"""
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
"""表单处理"""
form.instance.author = self.request.user
response = super().form_valid(form)
# 记录操作日志
self.log_creation()
return response
def get_success_url(self):
"""成功后的重定向"""
if self.request.POST.get('save_and_add_another'):
return reverse('post_create')
return super().get_success_url()
每种View类都有其特定的用途和方法重写点。理解这些方法的执行顺序和用途是掌握Django CBV的关键。