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

相关文章

来自专栏魂祭心

原 canvas绘制clock

4054
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

4848
来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

6768
来自专栏落花落雨不落叶

canvas画简单电路图

60911
来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

4708
来自专栏java 成神之路

使用 NIO 实现 echo 服务器

4607
来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

2707
来自专栏陈仁松博客

ASP.NET Core 'Microsoft.Win32.Registry' 错误修复

今天在发布Asp.net Core应用到Azure的时候出现错误InvalidOperationException: Cannot find compilati...

4838
来自专栏我和未来有约会

Kit 3D 更新

Kit3D is a 3D graphics engine written for Microsoft Silverlight. Kit3D was inita...

2526
来自专栏一个爱瞎折腾的程序猿

sqlserver使用存储过程跟踪SQL

USE [master] GO /****** Object: StoredProcedure [dbo].[sp_perfworkload_trace_s...

2050

扫码关注云+社区