66、rest_framework视图类

Django框架 / 2021-03-28

参考文章:刘清政《drf-视图组件》,原文链接:http://liuqingzheng.top/python/Django-rest-framework%E6%A1%86%E6%9E%B6/4-drf-%E8%A7%86%E5%9B%BE%E7%BB%84%E4%BB%B6/

一、rest_framework视图组件

Django REST framwork 提供的视图的主要作用:

  • 控制序列化器的执行(检验、保存、转换数据)
  • 控制数据库查询的执行

1.1、响应方式

上节学习中,我们发现当请求来自于浏览器时,rest_framework响应携带了HTML页面;当请求来自于postman时,只有json数据,这是怎么做到的呢?

其实是rest_framework会根据HTTP request里面的user-agent来判断是否为浏览器,这个配置是可以改的,且分全局和局部配置!

局部

仅对某个视图类有效

from rest_framework.renderers import JSONRenderer  # 先导入对应模块

renderer_classes = [JSONRenderer, ]  # 在视图类中加入此配置即可配置该视图类返回的一定是json数据而不带HTML页面。
全局

所有视图类都有效,在settings配置,里面都是drf的配置信息。

drf有自己的默认配置文件,查找顺序是先从类中找接着从项目的settings找,都没有再用自己默认的;后面的认证权限、频率校验等等都是在全局或者局部中配置。

# from rest_framework import settings  # drf默认配置文件

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',  # 需要全局生效JSON还是带HTML,只保留对应配置,其余的注释掉即可。
        # 'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}

1.2、视图基类

REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。

1.2.1、APIView

我们上节学习就是用的APIView,APIView是REST framework提供的所有视图的基类,继承自Django的View父类。视图类中的get、put等方法自己纯手写,有没有感觉很繁琐?来,看看下面的兄弟。

1.2.2、GenericAPIView

继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。

它简化了我们使用APIView时纯手写的方法,一起来看看。

通用视图类

GenericAPIView源码一开始就要定义的两个参数

queryset = None  # queryset对象,查询所有。

serializer_class = None  # 要使用的序列化类

先看看以前定义的视图类中的get方法代码吧

from rest_framework.generics import GenericAPIView  # 导入GenericAPIView


class ArticlesView(APIView):
    def get(self, request):
        article = models.Article.objects.all()
        article_obj = ArticleSeriallizer(instance=article, many=True)

        return Response(article_obj.data)

GenericAPIView里面定义了很多方法,可以完美替代我们之前手写的get、post等方法,解放双手!!!因直接贴源码篇幅太长,所以我这里只放出对应的方法和该方法的返回值,能看懂就行。

def get_queryset(self)
	...
	return queryset  # 这不就是我们之前手写的:article = models.Article.objects.all() 嘛!!!

def get_serializer_class(self):
    ...
    return self.serializer_class  # 这不就是之前手写的:article_obj = ArticleSeriallizer(instance=article, many=True) 嘛!!!

所以之前的所有方法可以改成下面这样了,主要是用self.get_object()替换models.Article.objects.filter(pk=pk).first;用self.get_queryset()替换models.Article.objects.all();用self.get_serializer_class()替换ArticleSeriallizer()!弄好一个,所有方法直接复制黏贴就行了,开心不!

class ArticleView(GenericAPIView):
    queryset = models.Article.objects.all()  # queryset对象,查询所有。
    serializer_class = ArticleSeriallizer  # 要使用的序列化类
    
    def get(self, request, pk):
        article = self.get_object()
        article_obj = self.get_serializer_class(instance=article)

        return Response(article_obj.data)

    def put(self, request, pk):
        callback_dict = {'status': 201, 'msg': 'sucess'}

        article = self.get_object()
        article_obj = self.get_serializer_class(instance=article, data=request.data)
        if article_obj.is_valid():
            article_obj.save()
            callback_dict['data'] = article_obj.data
        else:
            callback_dict['status'] = 400
            callback_dict['msg'] = 'invaild data'
            callback_dict['data'] = article_obj.errors
        return Response(callback_dict)

    def post(self, request):
        callback_dict = {'status': 201, 'msg': 'sucess'}

        article = self.get_serializer_class(data=request.data)
        if article.is_valid():
            article.save()
            callback_dict['data'] = article.data
        else:
            callback_dict['status'] = 400
            callback_dict['msg'] = 'invaild data'
            callback_dict['data'] = article.errors
        return Response(callback_dict)

    def delete(self, request, pk):
        response_dict = CallBackDict()
        delete_obj = self.get_object().delete()
        return Response(response_dict.callback_dict)


class ArticlesView(APIView):
    queryset = models.Article.objects.all()  # queryset对象,查询所有。
    serializer_class = ArticleSeriallizer  # 要使用的序列化类

    def get(self, request):
        article = self.get_queryset()
        article_obj = self.get_serializer_class(instance=article, many=True)

        return Response(article_obj.data)

你以为GenericAPIView就这点本事吗!太小看它了,来点有意思的。

5个视图扩展类

在views先导入以下模块

from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin

先看看ListModelMixin的源码,熟悉不?这不就是我们之前手写的方法!以此类推,从左到右为:查询所有、新增、修改、删除、查询单个

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

所以,以上手写的所有方法,在继承了五个视图子类后,可以简化成这样:

# 有没有感觉!这代码量少了好多!

class ArticleView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, CreateModelMixin, DestroyModelMixin):
    queryset = models.Article.objects.all()  # queryset对象,查询所有。
    serializer_class = ArticleSeriallizer  # 要使用的序列化类

    def get(self, request, pk):
        return self.retrieve(request, pk)  # 来自RetrieveModelMixin

    def put(self, request, pk):
        return self.update(request, pk)  # 来自UpdateModelMixin

    def post(self, request):
        return self.create(request)  # 来自CreateModelMixin

    def delete(self, request, pk):
        return self.destroy(request, pk)  # DestroyModelMixin

class ArticlesView(APIView, ListModelMixin):
    queryset = models.Article.objects.all()  # queryset对象,查询所有。
    serializer_class = ArticleSeriallizer  # 要使用的序列化类

    def get(self, request):
        return self.list(request)  # 来自ListModelMixin

上面通过继承五个子类后,我们的代码量得到大大精简,但是观察一下自己写的ArticleView和ArticlesView,会发现都有get方法重复了,虽然查询的是不一样的结果,能不能再封装一下呢?答案是有!

9个视图子类
视图子类提供的方法继承自
CreateAPIViewpostGenericAPIView、CreateModelMixin
ListAPIViewget(get所有)GenericAPIView、ListModelMixin
RetrieveAPIViewget(get单个)GenericAPIView、RetrieveModelMixin
DestoryAPIViewdeleteGenericAPIView、DestoryModelMixin
UpdateAPIViewput、patchGenericAPIView、UpdateModelMixin
RetrieveUpdateAPIViewget、put、patchGenericAPIView、RetrieveModelMixin、UpdateModelMixin
RetrieveUpdateDestoryAPIViewget、put、patch、deleteGenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
ListCreateAPIViewget、postListModelMixin、CreateModelMixin、GenericAPIView
RetrieveDestroyAPIViewget(单个)、deleteRetrieveModelMixin、DestroyModelMixin、GenericAPIView

所以,上面的ArticleView类的代码还可以写成这样

没错,就是这么三行代码就完事!膜拜大佬!

class ArticleView(RetrieveUpdateDestoryAPIView, CreateAPIView)
    queryset = models.Article.objects  # queryset对象
    serializer_class = ArticleSeriallizer  # 要使用的序列化类
世间微尘里 独爱茶酒中