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,减少代码重复。
  • 遵循单一职责原则:确保每个视图类只负责一个明确的任务。
  • 组织良好的代码结构:将视图类按功能模块化,便于管理和维护。
  • 处理权限和认证:合理配置权限和认证,确保应用的安全性。
  • 编写测试:为类视图编写测试,确保其行为符合预期。
相关推荐
运维-大白同学2 小时前
将django+vue项目发布部署到服务器
服务器·vue.js·django
喜欢猪猪2 小时前
Django:从入门到精通
后端·python·django
陈王卜4 小时前
django+boostrap实现发布博客权限控制
java·前端·django
vener_1 天前
LuckySheet协同编辑后端示例(Django+Channel,Websocket通信)
javascript·后端·python·websocket·django·luckysheet
mariokkm1 天前
Django一分钟:django中收集关联对象关联数据的方法
android·django·sqlite
qq_q9922502771 天前
django宠物服务管理系统
数据库·django·宠物
无忧无虑Coding1 天前
pyinstall 打包Django程序
后端·python·django
qq_q9922502772 天前
django基于python 语言的酒店推荐系统
后端·python·django
codists2 天前
《Django 5 By Example》阅读笔记:p679-p765
python·django
冷琴19962 天前
基于python+django+vue.js开发的停车管理系统
vue.js·python·django