目录

Django类视图CBV完全指南从入门到实战

目录

Django类视图(CBV)完全指南:从入门到实战

Django 各种View类需要实现的方法详解

我将详细解释每种Django类视图的核心方法、执行流程和需要重写的方法。

  1. 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)
  1. 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
  1. 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
  1. 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
  1. 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
  1. 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})
  1. 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
  1. 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)
  1. 各种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() 操作成功后 经常 重定向目标

  1. 实际应用示例
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的关键。