Skip to main content

基于类的视图2-通用视图

对于数据的增长改查,可以说是每个项目的通用操作。如果我们有多个模型,每个都这么写get、put、delete,那是非常麻烦的,也体现不出咱们的DRF(Django REST Framework)它的优势。我们能不能把一些相同的操作提取出来呢?比如说,在我们这里,我们在对数据进行操作的时候,首先来获取这个数据模型,然后还有一个序列化。如下图所示:

01获取模型

那么我们是不是可以把核心的内容给它提取出去,然后创建一个单独的类呢?我们能想到的,DRF 早已经为我们想到了。所以接下来我们就来介绍如何使用一个通用的视图。我们在movie/views.py文件中来新建一个也叫做 MovieDetail 这个类。在我们的原始数据中我们还需要对模型进行各种操作,增删改查。这个时候咱们需要引入一个新的类,这个类叫做 mixins 也是复合类。它可以对模型的各种操作,所以我们对模型操作直接导入这个 Mixins里面的已经写好类就可以执行这样的操作了。修改代码如下:

# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)

# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

# 定义get方法,用于获取电影信息
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)

# 定义put方法,用于更新电影信息
def put(self, request, *args, **kwargs):
return self.partial.update(request, *args, **kwargs)

# 定义delete方法,用于删除电影信息
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)

经过的我们的测试,上面的代码完全可以实现我们的功能,那上面的这些方法可以再进行简化吗?答案是肯定的,因为这些操作都是常见的。我们可以看一下如何使用更加简单的方式。还是在我们的我们在movie/views.py文件中来来修改,我们可以在generics.py中寻找能够替代这些操作的类。发现有RetrieveUpdateDestroyAPIView这个类,它相当于我们刚才继承了一系列类的情况。因为它的父类已经实现了这些操作,我们可以直接导入这个类,而无需再单独实现这些方法。这样操作后,代码量显著减少,但功能依然完整。修改代码如下:

# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)

# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(generics.RetrieveUpdateDestroyAPIView):

# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

接下来回到我们的postman中,我们尝试使用put方法来修改电影信息,当点击提交后,报错了,提示错误信息是电影的其他字段必须填写,如下图所示:

02字段必须填写

我们引入的是RetrieveUpdateDestroyAPIView这个类,我们找到这个源码发现它这里要是提交更改部分信息使用的是patch方法,如下图所示:

03patch方法.jpg

所以我们试一下把put修改为patch,点击send,这一次就修改成功了,这也是put和patch这两种方式的区别,所以说当我们有不同的需求的时候,比如说你要更改整个电影信息的话那么你就使用put方法,如果你只需要更改部分就使用patch方法。

如果我们想要修改MovieList方法,也可以采取相同的方法。我们首先注册这个视图,然后创建一个新的类来继承ListCreateAPIView,因为ListCreateAPIView已经包含了列表和新增的功能。接着我们只需要定义QuerySetSerializerClass,就完成了对该接口的定义。这种方式使得我们只需简单地选择合适的类名进行继承,而无需再手动编写大量代码。修改代码如下:

# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
class MovieList (generics.ListCreateAPIView):
queryset = Movie. objects.all()
serializer_class = MovieListSerializer
# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(generics.RetrieveUpdateDestroyAPIView):

# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

接下来我们再来测试这个接口能不能实现,回到我们的postman中,找咱们的查看电影列表,点击发送,服务器已经断开了,所以我们先打开我们的服务,这里报错了,如下图所示:

04报错 解决这个错误我们需要修改一下这个url,把movie_list改成MovieList,我们找到movie/urls.py文件,来修改代码,修改代码如下:

# 导入 Django 中的路径模块和 movie 应用中的视图模块
from django.urls import path
from movie import views

# 设置 app 的命名空间
app_name = 'movie'

# 定义 URL 路由列表
urlpatterns = [
# 定义空路径,对应电影列表视图,并设置名称为 'list'
path('', views.MovieList.as_view(), name='list'),
# 定义带有电影 ID 参数的路径,对应电影详情视图,并设置名称为 'detail'
path('<int:pk>/', views.MovieDetail.as_view(), name='detail'),
]

综上所述,我们使用了DRF中的通用视图类,通过继承相应的类,实现了各种接口功能。DRF的便利之处在于,我们只需定义少量代码,就能够实现完整的接口规范,包括列表查看、新增、修改和删除等功能。