使用Django 2.0构建Python Restful服务:五)优化创建模型序列化器和视图函数请求方法限制

在前面的章节中,我们在Django项目中创建了一个项目,并借助于django-rest-framework模块提供的序列化器、渲染器和解析器实现了restful风格的api。

这些结构和功能都还比较初级和原始,本章我们来优化一下我们的序列化器和视图函数。

5.1、优化序列化器

其中,我们的序列化器继承于serializers子模块的Serializer类:

# 文件路径:moviesapi/movies/serializers.py # coding:utf-8 from rest_framework import serializers from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser from movies.models import Movie  class MovieSerializer(serializers.Serializer):     # 声明模型的字段     pk = serializers.IntegerField(read_only=True)     name = serializers.CharField(max_length=30)     movie_cate = serializers.CharField(max_length=30)     movie_img = serializers.CharField(max_length=300)     release_date = serializers.DateField()     viewed = serializers.BooleanField(default=False)      # 声明模型创建的方法     def create(self, validated_data):         return Movie.objects.create(**validated_data)      # 声明模型更新的方法     def update(self, instance, validated_data):         instance.name = validated_data.get('name',instance.name)         instance.movie_cate = validated_data.get('movie_cate',instance.movie_cate)         instance.movie_img = validated_data.get('movie_img',instance.movie_img)         instance.release_date = validated_data.get('release_data',instance.release_date)         instance.viewed = validated_data.get('viewed',instance.viewed)         instance.save()         return instance 

Serializer这个类属于一个初加工的序列化器类,集成度并不是很高。之前介绍在rest_framework模块的serializers子模块中还有两个更加高级的序列化器类:

  • ModelSerializer
  • HyperlinkedModelSerializer

我们主要通过ModelSerializer()类来优化我们的序列化器。先来看看这个类的定义说明:

它的结构类似于我们在serializers.py中定义的MovieSerializer(),但是定义了更多的方法。我们可以借助这个类更加快速地定义一个新的序列化器。

我们在moviesapi/movies/serializers.py文件中新创建一个名为MovieSerializerNew,继承自serializers.ModelSerializer的类,然后重写它的Meta元数据,指定我们需要的模型和字段:

class MovieSerializerNew(serializers.ModelSerializer):     class Meta:         # 指定序列化器需要作用的模型         model = Movie         # 指定序列化器的模型字段         fields = (             'id',             'name',             'movie_cate',             'movie_img',             'release_date',             'viewed',             'created',         ) 

就这样简单的几句,就完成了第三章中使用了21行代码创建了序列化器MovieSerializer。

我们将moviesapi/movies/views.py文件中对应的视图函数中使用了MovieSerializer()序列化器的地方修改为MovieSerializerNew()序列化器:

# 电影列表资源 @csrf_exempt def movie_list(request):     if request.method == 'GET':         # 查询所有电影信息         movies = Movie.objects.all()         # 实例化一个序列化器         movies_serializer = MovieSerializerNew(movies,many=True)         # 返回序列化的json数据         return JsonResponse(movies_serializer.data)      elif request.method == 'POST':         # 解析http请求的数据         movie_data = JSONParser().parse(request)         # 实例化一个序列化器         movies_serializer = MovieSerializerNew(data=movie_data)         # 如果序列化数据有效         if movies_serializer.is_valid():             movies_serializer.save()             return JsonResponse(movies_serializer.data,status=status.HTTP_201_CREATED)         return JsonResponse(movies_serializer.errors,status=status.HTTP_400_BAD_REQUEST) 

使用Postman测试请求电影资源列表资源,效果与我们之前使用MovieSerializer()序列化器的效果是一样的:

# 电影详情资源 @csrf_exempt def movie_detail(request,pk):     # 首先判断是否存在相关数据     try:         movie = Movie.objects.get(pk=pk)     except Movie.DoesNotExist:         return HttpResponse(status=status.HTTP_404_NOT_FOUND)     # 获取电影详情资源     if request.method == 'GET':         movie_serializer = MovieSerializerNew(movie)         return JsonResponse(movie_serializer.data)      # 修改电影详情资源     elif request.method == 'PUT':         movie_data = JSONParser().parse(request)         # MovieSerializer()序列化器接收的参数的定义来源于其基类BaseSerializer         movie_serializer = MovieSerializerNew(movie,data=movie_data)         if movie_serializer.is_valid():             movie_serializer.save()             return JsonResponse(movie_serializer.data)         return JsonResponse(movie_serializer.errors,status=status.HTTP_400_BAD_REQUEST)      # 删除电影详情资源     elif request.method == 'DELETE':         movie.delete()         return HttpResponse(status=status.HTTP_204_NO_CONTENT) 

使用Postman测试请求电影资源详情资源,效果也与我们之前使用MovieSerializer()序列化器的效果是一样的:

这样,我们的序列化器就通过继承自serializers.ModelSerializer的MovieSerializerNew()类优化好了。

接下来,我们继续优化视图函数

5.2、优化视图函数

在我们之前的视图函数中,不同的视图函数操作方法通过对请求的method属性来判断。在电影资源列表视图函数movie_list()中,我们定义了GET和POST两个请求方法的逻辑处理,在电影资源详情视图函数movie_detail()中,我们定义了GET、PUT和DELETE三个请求方法的逻辑处理。

那么问题来了,如果我们使用了未定义的方法进行请求,那么其就会报错,情形如下图所示:

我们对电影详情使用了POST方法进行请求,结果报出了ValueError的错误来。

面对这种情形,我们可以在视图函数的if语句中添加一个else的代码块,返回一些自定义的提示,比如我们在电影详情的视图函数中添加else语句返回一个json错误提示:

# 电影详情资源 @csrf_exempt def movie_detail(request,pk):     # 首先判断是否存在相关数据     try:         movie = Movie.objects.get(pk=pk)     except Movie.DoesNotExist:         return HttpResponse(status=status.HTTP_404_NOT_FOUND)     # 获取电影详情资源     if request.method == 'GET':         movie_serializer = MovieSerializerNew(movie)         return JsonResponse(movie_serializer.data)      # 修改电影详情资源     elif request.method == 'PUT':         movie_data = JSONParser().parse(request)         # MovieSerializer()序列化器接收的参数的定义来源于其基类BaseSerializer         movie_serializer = MovieSerializerNew(movie,data=movie_data)         if movie_serializer.is_valid():             movie_serializer.save()             return JsonResponse(movie_serializer.data)         return JsonResponse(movie_serializer.errors,status=status.HTTP_400_BAD_REQUEST)      # 删除电影详情资源     elif request.method == 'DELETE':         movie.delete()         return HttpResponse(status=status.HTTP_204_NO_CONTENT)      else:         return JsonResponse({'error':'请求方法非法'}) 

使用Postman进行继续对id为13的电影进行post请求,其会返回一个json字符串,如下图所示:

除此之外,我们还能利用rest_framework模块提供的装饰器api_view方便的进行请求方法的规范定义。

其引入方法为:

from rest_framework.decorators import api_view 

这是一个将函数视图转换为rest_framework模块的APIView子类的装饰器,其接收一个列表,列表值为视图函数允许的请求方法,如下所示:

@api_view(['GET','POST']) 

如果使用了请求方法列表中未指定的请求方法,服务器就会返回405状态码以表示请求方法不被允许。

下面,我们对视图函数movie_list()和movie_detail()使用api_view装饰器进行修改:

# 电影列表资源 @api_view(['GET','POST']) def movie_list(request):     if request.method == 'GET':         # 查询所有电影信息         movies = Movie.objects.all()         # 实例化一个序列化器         movies_serializer = MovieSerializerNew(movies,many=True)         # 返回序列化的json数据         return JsonResponse(movies_serializer.data)      elif request.method == 'POST':         # 解析http请求的数据         movie_data = JSONParser().parse(request)         # 实例化一个序列化器         movies_serializer = MovieSerializerNew(data=movie_data)         # 如果序列化数据有效         if movies_serializer.is_valid():             movies_serializer.save()             return JsonResponse(movies_serializer.data,status=status.HTTP_201_CREATED)         return JsonResponse(movies_serializer.errors,status=status.HTTP_400_BAD_REQUEST) 

使用Postman测试电影列表不被允许的请求方法PUT,其结果返回了一个405状态码以及一个键为detail的json数据以说明细节,如下图所示:

接着是电影详情视图函数,修改后的代码如下所示:

# 电影详情资源 @api_view(['GET','PUT','DELETE']) def movie_detail(request,pk):     # 首先判断是否存在相关数据     try:         movie = Movie.objects.get(pk=pk)     except Movie.DoesNotExist:         return HttpResponse(status=status.HTTP_404_NOT_FOUND)     # 获取电影详情资源     if request.method == 'GET':         movie_serializer = MovieSerializerNew(movie)         return JsonResponse(movie_serializer.data)      # 修改电影详情资源     elif request.method == 'PUT':         movie_data = JSONParser().parse(request)         # MovieSerializer()序列化器接收的参数的定义来源于其基类BaseSerializer         movie_serializer = MovieSerializerNew(movie,data=movie_data)         if movie_serializer.is_valid():             movie_serializer.save()             return JsonResponse(movie_serializer.data)         return JsonResponse(movie_serializer.errors,status=status.HTTP_400_BAD_REQUEST)      # 删除电影详情资源     elif request.method == 'DELETE':         movie.delete()         return HttpResponse(status=status.HTTP_204_NO_CONTENT) 

使用Postman测试电影详情不被允许的请求方法POST,其结果返回了一个405状态码以及一个键为detail的json数据以说明细节,如下图所示:

这样,我们的视图函数也优化完成了。

5.3 本章小结

在本章,我们使用了django-rest-framework模块的serializers.ModelSerializer()类优化了我们的模型序列化器,使其结构更加精简的同时功能得到完善。

然后使用了django-rest-framwork模块的装饰器api_view对视图函数进行了优化,使得视图函数可以快捷的设置允许的请求方法和处理不被允许的请求方法。