快速创建Django模型序列化器和设置视图函数请求方法

【前文请自行回顾,赞赏和留言请直接下拉到页尾】

使用Django构建Python Restful Web服务开篇

使用Django构建Python Restful Web服务:二、生成数据模型

对Django模型对象进行序列化和反序列化

在Django 2.0中定义Web服务API接口

=== 正文 ===

在前面的章节中,我们在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属性来判断。在电影资源列表视图函数movielist()中,我们定义了GET和POST两个请求方法的逻辑处理,在电影资源详情视图函数moviedetail()中,我们定义了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字符串,如下图所示:

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

其引入方法为:

from rest_framework.decorators import api_view

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

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

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

下面,我们对视图函数movielist()和moviedetail()使用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对视图函数进行了优化,使得视图函数可以快捷的设置允许的请求方法和处理不被允许的请求方法。

原文发布于微信公众号 - 州的先生(zmister2016)

原文发表时间:2018-05-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

ASP.NET:创建Linked ValidationSummary, 深入理解ASP.NET的Validation

我想对于ASP.NET的Validator控件已经熟悉的不能再熟悉了。我们 已经习惯了用Validator控件来验证我们在表单的输入,并通过Validation...

2388
来自专栏IMWeb前端团队

React中如何不使用插件实现组件出现或消失动画

本文作者:IMWeb 结一 原文出处:IMWeb社区 未经同意,禁止转载 首先React本身是有动画插件的React.addons.Transitio...

3807
来自专栏Coco的专栏

Javascript正则构造函数与正则表达字面量&&常用正则表达式

1034
来自专栏Jackson0714

PHP内核之旅-3.变量

1064
来自专栏Jackson0714

C# 正则表达式

3545
来自专栏熊二哥

快速入门系列--MVC--06视图

到了View的呈现板块,感觉ASP.NET MVC的学习也进入了尾声,还是比较开心的,毕竟也有了不小收获。这部分内容相对比较简单,因为之前还专门学习过如何结合...

18010
来自专栏葡萄城控件技术团队

七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

? 系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据...

2405
来自专栏Jackson0714

PHP内核之旅-3.变量

2706
来自专栏Jackson0714

C# 正则表达式

962
来自专栏别先生

Javascript中String对象的的简单学习

第十一课 String对象介绍 1:属性     在javascript中可以用单引号,或者双引号括起来的一个字符当作     一个字符对象的实例,所以可以在某...

1887

扫码关注云+社区