快速创建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 条评论
登录 后参与评论

相关文章

来自专栏*坤的Blog

公司web安全等级提升

公司的一个web数据展示系统,本来是内网的,而且是一个单独的主机,不存在远程控制的问题,所以之前并没有考虑一些安全相关的测试.但是国调安全检查的需要添加这样子的...

1404
来自专栏一枝花算不算浪漫

[数据库连接池] Java数据库连接池--DBCP浅析.

37614
来自专栏Linux驱动

第1阶段——uboot分析之硬件初始化start.S(4)

分析uboot第一个执行函数_start(cpu/arm920t/start.S)  打开cpu/arm920t/start.S 1 .globl _start...

2198
来自专栏Linux驱动

35.Linux-分析并制作环形缓冲区

在上章34.Linux-printk分析、使用printk调试驱动里讲述了:  printk()会将打印信息存在内核的环形缓冲区log_buf[]里, 可以通...

2768
来自专栏SDNLAB

OpenDaylight Lithium版本简单应用及流表操作指南

OpenDaylight(以下简写为ODL)的Lithium(锂)版本的最新版Lithium-SR2已经与2015年10月8日发布,具体详情可参考ODL官网。L...

4578
来自专栏猿人谷

使用bash编写Linux shell脚本--复合命令

除了最简单的脚本,你很少想要执行每一个命令。执行一组命令或者重复执行一组命令若干次比执行单个命令更加有助。复合命令是将命令封装在一组其他命令中。 从可读性来说,...

29910
来自专栏向治洪

Android 使用android-support-multidex解决Dex超出方法数的限制问题

随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方sdk或者公共支持的jar包,项目耦合性高,重复作用的类越来越多),相信很多人都遇到过如下的...

3228
来自专栏深度学习与计算机视觉

如何打开ipynb文件

最近在学习《TensorFlow 实战GoogLe深度学习框架》这本书,发现里面提供的源码后缀是.ipynb,而不是.py,那么我们应该怎么打开.ipynb后缀...

4946
来自专栏大内老A

[WCF REST] 通过ASP.NET Output Caching实现声明式缓存

ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存。对于后续...

1886
来自专栏大内老A

[WCF REST] 通过ASP.NET Output Caching实现声明式缓存

ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存。对于后续...

1917

扫码关注云+社区