Skip to main content

基于类的视图1-使用APIView实现

上节课我们已经实现了一个基于函数的视图,在Django中还有基于类的视图。在之前的基础知识中,我们已经讲过了这一点。01两个视图

针对基于类的视图,Django REST Framework(DRF)提供了哪些扩展和支持呢?我们可以看一下DRF的文档手册。在文档中有关于API Guide,在视图部分,则包括了views、generic views以及viewsets。如下图所示:

02APIguide.jpg

这意味着DRF提供了多种方式来支持类的视图,这也是大多数小伙伴,特别是零基础的小伙伴,在学习Django DRF时的一个难点。为什么DRF要提供这么多方式呢?因为这个框架在设计之初,考虑到了灵活性和便利性。不同的情况可能需要不同的解决方式,但大多数操作都涉及到基本的增删改查功能。即使模型不同,操作方式也基本相同,只需稍作调整。因此,DRF提供了多种方式来满足不同需求。

在接下来的课程中,我们将介绍几种常用的方式,并解释它们之间的演变过程。在学习过程中,你只需要选择其中一种方式进行学习即可,具体选择哪种方式,取决于你的具体情况。首先,让我们来看看使用views的方式。在这种方式中,我们基于APIView这个视图类。如下图所示:

03apiview这个视图类.jpg

我们将不会修改上一节课的列表页,而是创建一个新的电影详情页。在movie/views.py中,我们首先从rest_framework中导入views,然后导入APIView。接着,我们创建一个类,命名为MovieDetail,并让它继承APIView。接下来,我们将实现增删改查操作方式。这些操作方式包括GET、POST、PUT、DELETE等方法。首先,我们来实现获取电影详情的方法,使用GET请求。在这个方法中,我们需要获取请求对象,并根据电影的ID来获取对应的电影详情。如果电影不存在,则返回404错误。修改代码如下:

# 从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
# 从当前目录下导入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类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

接下来,我们使用序列化将电影信息返回给前端。最后,我们需要在movie/serializers.py文件中定义一个MovieDetailSerializer来对电影详情进行序列化。代码如下:

# 导入rest_framework中的serializers模块
from rest_framework import serializers
# 导入Movie模型
from .models import Movie

# 定义MovieSerializer类,继承自ModelSerializer类
class MovieSerializer(serializers.ModelSerializer):

# 定义Meta类
class Meta:
# 指定要序列化的模型为Movie
model = Movie
# 指定序列化的字段为所有字段
fields = '__all__'

# 定义MovieDetailSerializer类,同样继承自ModelSerializer类
class MovieDetailSerializer(serializers.ModelSerializer):

# 定义Meta类
class Meta:
# 指定要序列化的模型为Movie
model = Movie
# 指定序列化的字段为所有字段
fields = '__all__'

接下来我们来运行看看这个效果。首先我们在postman中新增一个接口,命名为“电影详情”,使用GET方式。URL需要加上具体的ID,即您想查看的电影的ID。例如,如果要查看第二个电影的ID,则需要在URL后面加上对应的数字ID。例如http://127.0.0.1:8000/api/movie/2,点击发送后,由于我们还没有定义这个URL,会显示错误信息提示找不到。如下图所示:

04找不到页面

接下来,我们回到代码中,在URL中定义该路由。我们在movie/urls.py文件中来进行定义,这个路由是有一些特殊的,它是在基础URL的基础上加上了一个数字ID,这个数字是可变的,所以不能写死。我们可以使用“int:pk”来定义这个参数。然后,在视图中我们将使用MovieDetail类,并将其转化为可以执行的视图对象。同时,给它起一个别名“Detail”。代码如下:

# 导入django.urls中的path函数
from django.urls import path
# 导入views模块中的MovieList和MovieDetail类
from movie import views

# 定义app名称为'movie'
app_name = 'movie'

# 定义URL模式列表
urlpatterns = [
# 定义空路径对应的视图为MovieList,并命名为'list'
path('', views.MovieList.as_view(), name='list'),
# 定义带有整数参数pk的路径对应的视图为MovieDetail,并命名为'detail'
path('<int:pk>/', views.MovieDetail.as_view(), name='detail'),
]

接着,我们访问这个新增的接口,通过在基础URL后加上一个pk来访问特定电影的信息。通常我们会在后面加上一个斜杠,然后加上特定电影的ID来发送请求。如果成功,服务器将输出对应ID的电影信息。如下图所示:

05id为2的电影信息

接下来,我们将修改电影信息的功能添加到接口中。在movie/views.py中我们使用PUT方法来进行修改,同样需要指定一个ID来确定要修改的电影。首先,我们判断电影是否存在,如果不存在则报错。然后,我们使用序列化器对用户提交的数据进行验证。验证通过后,进行提交操作。修改代码如下

# 从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
# 从当前目录下导入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类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

# 定义put方法,接收请求、电影主键pk作为参数
def put(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化,传入请求数据
serializer = MovieDetailSerializer(movie, data=request.data,partial=True)
# 如果序列化器验证通过
if serializer.is_valid():
# 保存序列化后的数据
serializer.save()
# 返回成功的响应,状态码为HTTP 202 ACCEPTED
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
# 如果验证不通过,返回错误信息响应,状态码为HTTP 400 BAD REQUEST
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

回到我们的postman中,我们新建一个put请求,修改电影信息,把人生大事这个电影名字修改成人生小事,如下图所示:

06修改电影信息.jpg

同样的流程适用于删除功能,使用DELETE方法来删除电影信息。同样需要指定一个ID来确定要删除的电影。首先判断电影是否存在,存在则进行删除操作,然后返回相应的响应码。修改代码如下:

# 从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
# 从当前目录下导入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类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

# 定义put方法,接收请求、电影主键pk作为参数
def put(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化,传入请求数据
serializer = MovieDetailSerializer(movie, data=request.data,partial=True)
# 如果序列化器验证通过
if serializer.is_valid():
# 保存序列化后的数据
serializer.save()
# 返回成功的响应,状态码为HTTP 202 ACCEPTED
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
# 如果验证不通过,返回错误信息响应,状态码为HTTP 400 BAD REQUEST
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# 定义delete方法,接收请求和电影主键pk作为参数
def delete(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 删除电影对象
movie.delete()
# 返回成功的响应,状态码为HTTP 204 NO CONTENT
return Response(status=status.HTTP_204_NO_CONTENT)

回到我们的postman中,我们再新建一个删除请求,删除id为533的电影,如下图所示:

07删除533电影

我们实现了查看、修改和删除功能,使用了继承自APIView的视图类,并创建了不同的方法来实现不同的功能。这是操作视图的一种方式,即继承APIView类。