Django类视图CBV

类视图(Class-Based Views,简称 CBV)是 Django 中构建视图的一种强大且灵活的方式。相比于函数视图(Function-Based Views,FBV),类视图提供了更好的可复用性和可扩展性,尤其在处理复杂逻辑和大型项目时尤为有用。本文将详细讲解 Django 中的类视图,包括内置类视图、自定义类视图的设计,以及如何在 Django REST framework 中使用类视图。

文章目录

    • [1. 类视图简介](#1. 类视图简介)
      • [1.1 函数视图 vs 类视图](#1.1 函数视图 vs 类视图)
      • [1.2 优势](#1.2 优势)
    • [2. Django 内置类视图](#2. Django 内置类视图)
      • [2.1 基础类视图](#2.1 基础类视图)
        • [2.1.1 `View`](#2.1.1 View)
        • [2.1.2 `TemplateView`](#2.1.2 TemplateView)
        • [2.1.3 `RedirectView`](#2.1.3 RedirectView)
      • [2.2 通用类视图(Generic Views)](#2.2 通用类视图(Generic Views))
        • [2.2.1 `ListView`](#2.2.1 ListView)
        • [2.2.2 `DetailView`](#2.2.2 DetailView)
        • [2.2.3 `CreateView`](#2.2.3 CreateView)
        • [2.2.4 `UpdateView`](#2.2.4 UpdateView)
        • [2.2.5 `DeleteView`](#2.2.5 DeleteView)
      • [2.3 组合使用通用视图](#2.3 组合使用通用视图)
    • [3. 自定义类视图](#3. 自定义类视图)
      • [3.1 设计思路](#3.1 设计思路)
      • [3.2 示例:自定义搜索视图](#3.2 示例:自定义搜索视图)
      • [3.3 使用 Mixins 增强类视图](#3.3 使用 Mixins 增强类视图)
      • [3.4 自定义表单处理](#3.4 自定义表单处理)
    • [4. 类视图在 Django REST framework 中的应用](#4. 类视图在 Django REST framework 中的应用)
      • [4.1 基础类视图](#4.1 基础类视图)
        • [4.1.1 `APIView`](#4.1.1 APIView)
      • [4.2 通用类视图](#4.2 通用类视图)
        • [4.2.1 `ListAPIView` 和 `CreateAPIView`](#4.2.1 ListAPIViewCreateAPIView)
        • [4.2.2 `RetrieveAPIView`, `UpdateAPIView`, `DestroyAPIView`](#4.2.2 RetrieveAPIView, UpdateAPIView, DestroyAPIView)
        • [4.2.3 `ListCreateAPIView` 和 `RetrieveUpdateDestroyAPIView`](#4.2.3 ListCreateAPIViewRetrieveUpdateDestroyAPIView)
      • [4.3 视图集(ViewSets)](#4.3 视图集(ViewSets))
      • [4.4 自定义类视图](#4.4 自定义类视图)
    • [5. 类视图的最佳实践](#5. 类视图的最佳实践)
      • [5.1 遵循单一职责原则](#5.1 遵循单一职责原则)
      • [5.2 使用通用类视图和 Mixins](#5.2 使用通用类视图和 Mixins)
      • [5.3 合理组织代码结构](#5.3 合理组织代码结构)
      • [5.4 使用装饰器和 Mixins 添加额外功能](#5.4 使用装饰器和 Mixins 添加额外功能)
      • [5.5 优化 `get_queryset` 和 `get_serializer_class`](#5.5 优化 get_querysetget_serializer_class)
      • [5.6 处理异常和错误](#5.6 处理异常和错误)
    • [6. 实战示例:构建一个完整的 CRUD API](#6. 实战示例:构建一个完整的 CRUD API)
      • [6.1 定义模型](#6.1 定义模型)
      • [6.2 创建序列化器](#6.2 创建序列化器)
      • [6.3 实现视图集](#6.3 实现视图集)
      • [6.4 配置路由](#6.4 配置路由)
      • [6.5 配置权限和认证](#6.5 配置权限和认证)
      • [6.6 运行和测试 API](#6.6 运行和测试 API)
    • [7. 总结](#7. 总结)

1. 类视图简介

1.1 函数视图 vs 类视图

函数视图(FBV) 是 Django 中最基本的视图类型,通过定义一个函数来处理请求并返回响应。例如:

python 复制代码
# myapp/views.py
from django.http import HttpResponse

def hello_world(request):
    return HttpResponse("Hello, World!")

类视图(CBV) 则使用类来定义视图,通过继承 Django 提供的基类,实现不同的 HTTP 方法(如 GET、POST 等)。例如:

python 复制代码
# myapp/views.py
from django.views import View
from django.http import HttpResponse

class HelloWorldView(View):
    def get(self, request):
        return HttpResponse("Hello, World!")

1.2 优势

  • 可复用性:通过继承和组合,可以轻松复用代码。
  • 组织性:将相关逻辑封装在一个类中,使代码结构更清晰。
  • 扩展性:便于添加新的功能或修改现有功能。
  • 内置功能丰富:Django 提供了许多内置的通用类视图,简化常见任务。

2. Django 内置类视图

Django 提供了许多内置的类视图,涵盖了常见的操作,如展示模板、处理表单、执行 CRUD 操作等。以下是一些常用的内置类视图及其用法。

2.1 基础类视图

2.1.1 View

django.views.View 是所有类视图的基类。它提供了处理不同 HTTP 方法的基本结构。

python 复制代码
# myapp/views.py
from django.views import View
from django.http import HttpResponse

class MyView(View):
    def get(self, request):
        return HttpResponse('GET 请求')

    def post(self, request):
        return HttpResponse('POST 请求')
2.1.2 TemplateView

用于渲染模板并返回响应,适用于简单的页面展示。

python 复制代码
# myapp/views.py
from django.views.generic import TemplateView

class HomePageView(TemplateView):
    template_name = 'home.html'
python 复制代码
# myapp/urls.py
from django.urls import path
from .views import HomePageView

urlpatterns = [
    path('', HomePageView.as_view(), name='home'),
]
2.1.3 RedirectView

用于重定向到其他 URL。

python 复制代码
# myapp/views.py
from django.views.generic import RedirectView

class OldHomePageRedirect(RedirectView):
    url = '/new-home/'
python 复制代码
# myapp/urls.py
from django.urls import path
from .views import OldHomePageRedirect

urlpatterns = [
    path('old-home/', OldHomePageRedirect.as_view(), name='old_home'),
]

2.2 通用类视图(Generic Views)

通用类视图提供了更高级别的抽象,适用于常见的数据库操作,如显示列表、创建、更新和删除对象。

2.2.1 ListView

显示对象的列表。

python 复制代码
# myapp/views.py
from django.views.generic import ListView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'
    context_object_name = 'posts'
    ordering = ['-created_at']
2.2.2 DetailView

显示单个对象的详细信息。

python 复制代码
# myapp/views.py
from django.views.generic import DetailView
from .models import Post

class PostDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
2.2.3 CreateView

创建新对象的表单视图。

python 复制代码
# myapp/views.py
from django.views.generic import CreateView
from .models import Post
from django.urls import reverse_lazy

class PostCreateView(CreateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'post_form.html'
    success_url = reverse_lazy('post_list')
2.2.4 UpdateView

更新现有对象的表单视图。

python 复制代码
# myapp/views.py
from django.views.generic import UpdateView
from .models import Post
from django.urls import reverse_lazy

class PostUpdateView(UpdateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'post_form.html'
    success_url = reverse_lazy('post_list')
2.2.5 DeleteView

删除对象的确认视图。

python 复制代码
# myapp/views.py
from django.views.generic import DeleteView
from .models import Post
from django.urls import reverse_lazy

class PostDeleteView(DeleteView):
    model = Post
    template_name = 'post_confirm_delete.html'
    success_url = reverse_lazy('post_list')

2.3 组合使用通用视图

可以通过组合多个通用视图,快速实现复杂的功能。例如,使用 ListViewDetailView 实现一个博客的文章列表和详细页面。

python 复制代码
# myapp/views.py
from django.views.generic import ListView, DetailView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'
    context_object_name = 'posts'
    ordering = ['-created_at']

class PostDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
python 复制代码
# myapp/urls.py
from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('', PostListView.as_view(), name='post_list'),
    path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]

3. 自定义类视图

虽然 Django 提供了许多内置类视图,但在实际开发中,往往需要根据具体需求自定义类视图。下面介绍如何设计和实现自定义类视图。

3.1 设计思路

  1. 继承自适当的基类 :根据需要继承自 ViewTemplateViewListView 等。
  2. 定义必要的方法 :如 getpost 等,或者使用类视图提供的钩子方法(如 get_context_data)。
  3. 封装通用逻辑:通过方法的重写或添加辅助方法,封装通用逻辑,提高代码复用性。

3.2 示例:自定义搜索视图

假设我们希望在 PostListView 中添加搜索功能,根据标题关键字过滤文章。

python 复制代码
# myapp/views.py
from django.views.generic import ListView
from .models import Post
from django.db.models import Q

class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'
    context_object_name = 'posts'
    ordering = ['-created_at']
    paginate_by = 10

    def get_queryset(self):
        queryset = super().get_queryset()
        query = self.request.GET.get('q')
        if query:
            queryset = queryset.filter(Q(title__icontains=query) | Q(content__icontains=query))
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['q'] = self.request.GET.get('q', '')
        return context
html 复制代码
<!-- templates/post_list.html -->
<form method="get">
    <input type="text" name="q" placeholder="搜索..." value="{{ q }}">
    <button type="submit">搜索</button>
</form>

<ul>
    {% for post in posts %}
        <li><a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a></li>
    {% endfor %}
</ul>

{% if is_paginated %}
    <div>
        {% if page_obj.has_previous %}
            <a href="?q={{ q }}&page={{ page_obj.previous_page_number }}">上一页</a>
        {% endif %}
        <span>第 {{ page_obj.number }} 页,共 {{ page_obj.paginator.num_pages }} 页</span>
        {% if page_obj.has_next %}
            <a href="?q={{ q }}&page={{ page_obj.next_page_number }}">下一页</a>
        {% endif %}
    </div>
{% endif %}

3.3 使用 Mixins 增强类视图

Mixin 是一种通过多继承来复用代码的设计模式。在 Django 类视图中,Mixins 可以用来添加额外的功能,而不需要修改现有的视图类。

示例:添加登录验证

假设我们希望某些视图只能由登录用户访问,可以创建一个 Mixin 来实现这一功能。

python 复制代码
# myapp/mixins.py
from django.contrib.auth.mixins import LoginRequiredMixin

# Django 已经提供了 LoginRequiredMixin,无需自定义
# 如果需要自定义,可以这样做:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

class CustomLoginRequiredMixin:
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

然后在视图中使用该 Mixin:

python 复制代码
# myapp/views.py
from django.views.generic import ListView
from .models import Post
from .mixins import CustomLoginRequiredMixin  # 如果使用自定义的 Mixin
from django.contrib.auth.mixins import LoginRequiredMixin  # 推荐使用 Django 内置的

class ProtectedPostListView(LoginRequiredMixin, ListView):
    model = Post
    template_name = 'protected_post_list.html'
    context_object_name = 'posts'
    login_url = '/login/'

3.4 自定义表单处理

假设我们需要一个自定义的表单视图,不仅处理表单提交,还需要在表单验证失败时执行特定逻辑。

python 复制代码
# myapp/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content']
python 复制代码
# myapp/views.py
from django.views.generic import View
from django.shortcuts import render, redirect
from .forms import PostForm

class CustomPostCreateView(View):
    form_class = PostForm
    template_name = 'post_form.html'

    def get(self, request):
        form = self.form_class()
        return render(request, self.template_name, {'form': form})

    def post(self, request):
        form = self.form_class(request.POST)
        if form.is_valid():
            form.save()
            return redirect('post_list')
        else:
            # 自定义处理逻辑,例如记录错误日志
            return render(request, self.template_name, {'form': form})

4. 类视图在 Django REST framework 中的应用

Django REST framework(DRF)同样广泛使用类视图,提供了更高层次的抽象,简化 API 的开发。下面介绍 DRF 中常用的类视图及其使用方法。

4.1 基础类视图

4.1.1 APIView

APIView 是 DRF 中所有类视图的基类,类似于 Django 的 View。它提供了对 HTTP 方法的支持,并集成了 DRF 的功能,如认证、权限、序列化等。

python 复制代码
# myapp/api_views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class HelloWorldAPIView(APIView):
    def get(self, request):
        return Response({"message": "Hello, World!"}, status=status.HTTP_200_OK)

    def post(self, request):
        data = request.data
        return Response({"received_data": data}, status=status.HTTP_201_CREATED)

4.2 通用类视图

DRF 提供了许多通用类视图,简化了常见的 API 操作,如列表、创建、检索、更新和删除。

4.2.1 ListAPIViewCreateAPIView

分别用于列出对象和创建新对象。

python 复制代码
# myapp/api_views.py
from rest_framework.generics import ListAPIView, CreateAPIView
from .models import Post
from .serializers import PostSerializer

class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostCreateAPIView(CreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
4.2.2 RetrieveAPIView, UpdateAPIView, DestroyAPIView

分别用于检索、更新和删除单个对象。

python 复制代码
# myapp/api_views.py
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView

class PostRetrieveAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostUpdateAPIView(UpdateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostDestroyAPIView(DestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
4.2.3 ListCreateAPIViewRetrieveUpdateDestroyAPIView

将多个操作组合在一起,减少代码重复。

python 复制代码
# myapp/api_views.py
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView

class PostListCreateAPIView(ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

4.3 视图集(ViewSets)

视图集将多个相关的视图操作组合在一个类中,进一步简化代码。DRF 提供了 ModelViewSetReadOnlyModelViewSet 等。

python 复制代码
# myapp/api_views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
python 复制代码
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import PostViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

4.4 自定义类视图

在 DRF 中,也可以根据需要自定义类视图,实现特定的逻辑。

示例:自定义权限和过滤
python 复制代码
# myapp/api_views.py
from rest_framework import viewsets, permissions, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Post
from .serializers import PostSerializer

class CustomPostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['title', 'created_at']
    search_fields = ['title', 'content']
    ordering_fields = ['created_at', 'title']
    ordering = ['-created_at']

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

5. 类视图的最佳实践

为了确保类视图的优雅和高效,遵循以下最佳实践是非常重要的。

5.1 遵循单一职责原则

每个视图类应专注于一个特定的任务,避免在一个视图中处理过多的逻辑。这有助于提高代码的可维护性和可读性。

5.2 使用通用类视图和 Mixins

尽可能使用 Django 或 DRF 提供的通用类视图和 Mixins,避免重复造轮子。这不仅减少了代码量,还利用了框架的优化和最佳实践。

python 复制代码
# 示例:使用 DRF 的 ListModelMixin 和 CreateModelMixin
from rest_framework import viewsets, mixins

class PostViewSet(mixins.ListModelMixin,
                 mixins.CreateModelMixin,
                 viewsets.GenericViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

5.3 合理组织代码结构

将视图类按照功能或模块组织在不同的文件中,避免单个文件过于庞大。例如,可以将 API 视图和前端视图分开存放。

复制代码
myapp/
├── views/
│   ├── __init__.py
│   ├── frontend_views.py
│   └── api_views.py
├── models.py
├── serializers.py
├── urls.py
└── ...

5.4 使用装饰器和 Mixins 添加额外功能

如权限控制、缓存、速率限制等,可以通过装饰器或 Mixins 添加到类视图中,而不需要修改视图的核心逻辑。

python 复制代码
# 示例:添加速率限制
from rest_framework.throttling import UserRateThrottle
from rest_framework import viewsets

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    throttle_classes = [UserRateThrottle]

5.5 优化 get_querysetget_serializer_class

根据请求的不同,动态调整查询集和序列化器类,提升性能和灵活性。

python 复制代码
# 示例:根据用户角色使用不同的序列化器
class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()

    def get_serializer_class(self):
        if self.request.user.is_staff:
            return PostAdminSerializer
        return PostSerializer

5.6 处理异常和错误

在类视图中适当处理可能出现的异常,确保 API 的稳定性和用户体验。

python 复制代码
# 示例:处理对象不存在的异常
from rest_framework.exceptions import NotFound

class PostDetailAPIView(APIView):
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            raise NotFound(detail="Post not found", code=404)

    def get(self, request, pk):
        post = self.get_object(pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)

6. 实战示例:构建一个完整的 CRUD API

通过一个完整的示例,展示如何使用类视图构建一个 CRUD(创建、读取、更新、删除)API。

6.1 定义模型

python 复制代码
# myapp/models.py
from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

6.2 创建序列化器

python 复制代码
# myapp/serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    author = serializers.ReadOnlyField(source='author.username')

    class Meta:
        model = Post
        fields = ['id', 'author', 'title', 'content', 'created_at']

6.3 实现视图集

python 复制代码
# myapp/api_views.py
from rest_framework import viewsets, permissions
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    """
    一个完整的 Post CRUD API
    """
    queryset = Post.objects.all().order_by('-created_at')
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

6.4 配置路由

python 复制代码
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import PostViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')

urlpatterns = [
    path('api/', include(router.urls)),
]

6.5 配置权限和认证

settings.py 中配置 DRF 的权限和认证机制。

python 复制代码
# myproject/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ],
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

确保安装并添加 django-filter

bash 复制代码
pip install django-filter

INSTALLED_APPS 中添加 'django_filters'

python 复制代码
# myproject/settings.py
INSTALLED_APPS = [
    # 其他应用...
    'rest_framework',
    'django_filters',
    'myapp',
]

6.6 运行和测试 API

启动开发服务器:

bash 复制代码
python manage.py runserver

访问 /api/posts/ 可以查看所有帖子,支持 GET、POST 等方法。通过浏览器或工具(如 Postman)测试 API 的各项功能。

7. 总结

类视图(CBV)在 Django 和 Django REST framework 中提供了强大且灵活的方式来构建视图和 API。通过继承内置类视图、使用 Mixins 以及自定义视图类,可以大大提高代码的可复用性和可维护性。以下是关键点总结:

  • 选择合适的基类 :根据需求选择 ViewTemplateViewListView 等内置类视图。
  • 利用通用类视图:使用 Django 和 DRF 提供的通用类视图和 Mixins,减少代码重复。
  • 遵循单一职责原则:确保每个视图类只负责一个明确的任务。
  • 组织良好的代码结构:将视图类按功能模块化,便于管理和维护。
  • 处理权限和认证:合理配置权限和认证,确保应用的安全性。
  • 编写测试:为类视图编写测试,确保其行为符合预期。
相关推荐
blues_C1 天前
十三、【核心功能篇】测试计划管理:组织和编排测试用例
vue.js·django·测试用例·drf·测试平台
恸流失2 天前
DJango项目
后端·python·django
编程大全2 天前
41道Django高频题整理(附答案背诵版)
数据库·django·sqlite
网安小张2 天前
解锁FastAPI与MongoDB聚合管道的性能奥秘
数据库·python·django
KENYCHEN奉孝2 天前
Pandas和Django的示例Demo
python·django·pandas
老胖闲聊2 天前
Python Django完整教程与代码示例
数据库·python·django
noravinsc2 天前
django paramiko 跳转登录
后端·python·django
践行见远2 天前
django之请求处理过程分析
数据库·django·sqlite
声声codeGrandMaster2 天前
Django之表格上传
后端·python·django
菌菌的快乐生活2 天前
网站静态文件加速-Django项目静态文件存储到腾讯云COS存储提升网络请求速度
django·cos存储