第三篇:DRF之视图组件

第三篇:DRF之视图组件

一、请求和响应

1、请求

我们再看一遍源码,使用from rest_framework.request import Request导入Request。内部实现如下所示。

下面,确保了可以按照原生的request取数据。

"""重要方法""" 【这里的request是新的request】
# request.data
前端以三种编码方式传入的数据,都可以取出来,是一个字典
# request.query_params
与Django标准的request.GET相同,只是更换了更正确的名称而已
# request.FILES
取出以formdata,传过来的文件数据,【其实在request.data也可以拿到】

2、响应

使用from rest_framework.response import Response,查看源码。我们发现Response并不是只可以传入一个data,而是可以传很多数据。

# data
要返回的数据,一般以字典格式
# status
返回的状态码,默认是200【正确】
我们可以通过  from rest_framework import status 查看所有状态码
在这个路径下,它把所有使用到的状态码都定义成了常量
# template_name
渲染的模板名字(自定制模板),不需要了解【就是返回给浏览器的页面】
# headers
响应头,可以往响应头中放数据,就是一个字典。
【原生django以什么方式往响应头中存放数据?直接在httpresponse中以参数传入即可】
# exception
异常处理相关
# content_type
响应的编码格式,application/json和text/html;
【postman中是,application/json;而浏览器中是text/html】

那么引出一个问题,浏览器和postman是怎么显示不同的央视的呢?内部是如何区分的呢?

浏览器响应成浏览器的格式,而postman响应成json格式,是通过配置实现的(内部的默认配置便是如此),那么我们如何进行修改REST_FRAMEWORK的默认配置的呢?

3、配置REST_FRAMEWORK参数

1、全局配置

我们在settings.py文件中,书写这样的配置。

# 配置参数
REST_FRAMEWORK = {
        # 默认响应渲染类
        'DEFAULT_RENDERER_CLASSES': ( 
            'rest_framework.renderers.JSONRenderer',  # json渲染器
            'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
        )
}

如果希望浏览器返回json格式数据,我们只需要将第二栏注释掉即可。

2、局部配置

那么我们想要将某一个视图函数进行相关配置,那么就需要用到局部配置。

方式如下。

from rest_framework.renderers import JSONRenderer
# 测试渲染1 【如何只去掉这个视图类的浏览器渲染?】
class TestBro1(APIView):
    renderer_classes = [JSONRenderer]  # 局部配置
    def get(self, request):
        return Response(
            data={'name': '杨毅', 'age': 18},
            status=status.HTTP_200_OK,
            headers={'test': '666'}
        )

总结:drf的相关配置信息,先从自己类中找 ----> 项目的setting中找 ----> 到drf默认的配置文件中找。

二、视图组件

1、基于APIVIew写接口

我们观看一下以前基于APIVIew写的5个接口。【分别为 查看一个、查看所有、修改、新增、删除】。

"""urls.py"""
# 基于基于APIVIew写接口写的5个接口
url(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),  # 有名分组和无名分组之后必须传入一个参数,如pk
url(r'books/', views.BooksView.as_view())


"""ser.py"""
from rest_framework import serializers
from app01 import models

# 创建序列化组件
class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
       
    
"""models.py"""
from django.db import models

# 创建书籍表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    
    
"""views.py"""
"""基于APIView实现5个接口"""
class BookView(APIView):
    # 查看一个数据
    def get(self, request, pk):
        back_dic = {'status': 200, 'msg': '查看成功'}
        # 根据pk拿到数据对象
        book_obj = models.Book.objects.filter(id=pk).first()
        # 序列化
        book_ser = BookSerializers(book_obj)
        # 返回数据
        back_dic['data'] = book_ser.data
        # 返回
        return Response(back_dic)

    # 修改一个数据
    def put(self, request, pk):
        back_dic = {'status': 200, 'msg': '修改成功'}
        # 根据pk拿到数据对象
        book_obj = models.Book.objects.filter(id=pk).first()
        # 序列化【继承ModelSerializer不需要重写update方法】
        book_ser = BookSerializers(instance=book_obj, data=request.data)
        # 判断是否成功
        if book_ser.is_valid():
            # 返回数据
            book_ser.save()  # 保存数据
            back_dic['data'] = book_ser.data
        else:
            back_dic['status'] = 101
            back_dic['msg'] = '修改失败'
        # 返回
        return Response(back_dic)

    # 删除一个数据
    def delete(self, request, pk):
        back_dic = {'status': 200, 'msg': '删除成功'}
        # 根据pk拿到数据对象
        models.Book.objects.filter(id=pk).delete()
        return Response(back_dic)


class BooksView(APIView):
    # 查看所有数据
    def get(self, request):
        back_dic = {'status': 200, 'msg': '获取全部数据成功'}
        # 拿到所有的数据
        book_queryset = models.Book.objects.all()
        # 序列化
        book_ser = BookSerializers(book_queryset, many=True)
        # 添加数据
        back_dic['data'] = book_ser.data
        # 返回数据
        return Response(back_dic)

    # 新增一个数据
    def post(self, request):
        back_dic = {'status': 200, 'msg': '新增数据成功'}
        # 操作【不需要书写create方法】
        book_ser = BookSerializers(data=request.data)
        if book_ser.is_valid():
            book_ser.save()  # 保存数据
            back_dic['data'] = book_ser.data
        else:
            back_dic['status'] = 102
            back_dic['msg'] = '新增数据失败'
        # 返回数据
        return Response(back_dic)

2、基于GenericAPIView写5个接口

我们虽然可以通过APIView实现5个接口,但是我们发现代码过于冗余,有没有更简便的方法呢?GenericAPIView进行了一定的优化。

具体代码如下。

  • urls.py
# 基于GenericAPIView写的
url(r'^books2/(?P<pk>\d+)', views.BookDetailView2.as_view()),
url(r'^books2/', views.BookView2.as_view())
  • views.py
from rest_framework.generics import GenericAPIView
"""基于GenericAPIView写的5个接口"""
# GenericAPIView ---> APIView ---> View ---> objects ---> type
class BookView2(GenericAPIView):
    # 观看源码 queryset = models.Book.objects 也可以
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers

    # 获取所有的数据
    def get(self, request):
        # 代替 book_queryset = models.Book.objects.all()
        book_queryset = self.get_queryset()
        # 代替 book_ser = BookSerializers(book_queryset, many=True)
        book_ser = self.get_serializer(book_queryset, many=True)  # 本质还是执行上面
        # 返回数据
        return Response(book_ser.data)

    # 新增一个数据
    def post(self, request):
        # 代替 book_ser = BookSerializers(data=request.data)
        book_ser = self.get_serializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': '校验失败'})


class BookDetailView2(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers
    # 获取一个数据
    def get(self, request, pk):
        # 代替 book_obj = models.Book.objects.filter(id=pk).first()
        book_obj = self.get_object()  # 得到一个数据对象
        # 代替 book_ser = BookSerializers(book_obj)
        book_ser = self.get_serializer(book_obj)
        # 返回数据
        return Response(book_ser.data)

    # 修改一个数据
    def put(self, request, pk):
        # 代替 book_obj = models.Book.objects.filter(id=pk).first()
        book_obj = self.get_object()
        # 代替 book_ser = BookSerializers(instance=book_obj, data=request.data)
        book_ser = self.get_serializer(instance=book_obj, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': '校验失败'})

    # 删除一个数据
    def delete(self, request, pk):
        # 代替 models.Book.objects.filter(id=pk).delete()
        self.get_object().delete()
        return Response({'status': 100, 'msg': '删除成功'})

总结:

"""视图类开始"""
# 拿到所有数据的查询集
queryset = models.Book.objects.all()
# 拿到序列化的类
serializer_class = BookSerializers

"""操作"""
# 拿到所有的数据
book_queryset = self.get_queryset()
# 拿到一个数据对象
book_obj = self.get_object()    # 不需要传参,会自动使用pk
# 序列化方式统一
self.get_serializer()   ===  BookSerializers()

3、基于GenericAPIView和5个视图扩展类写的接口

现在我们已经可以将代码进行所谓的"工整化",但是代码仍然太过于冗余,我们怎么再进一步进行简化?这就要用到了5个视图扩展类,具体使用方式如下。

  • urls.py
# 基于GenericAPIView和5个视图扩展类
url(r'^books3/(?P<pk>\d+)', views.BookDetailView3.as_view()),
url(r'^books3/', views.BookView3.as_view())
  • views.py
"""基于GenericAPIView和5个视图扩展类写的接口"""  # 5个视图扩展类每个写了一个方法【如:list】
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
class BookView3(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers
    # 获取所有的数据【将上面基于GenericAPIView的代码再次进行封装】
    def get(self, request):
        return self.list(request)
    # 新增一个数据
    def post(self, request):
        return self.create(request)

class BookDetailView3(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers
    # 获取一个数据
    def get(self, request, pk):
        return self.retrieve(request, pk)
    # 新增一个数据
    def put(self, request, pk):
        return self.update(request, pk)
    # 删除一个数据
    def delete(self, request, pk):
        return self.destroy(request, pk)

4、基于GenericAPIView同级的9个视图类

到了这里,我们可以更进一步进行封装,方式如下。

  • urls.py
# 基于GenericAPIView同级的9个视图类
url(r'^books4/(?P<pk>\d+)', views.BookDetailView4.as_view()),
url(r'^books4/', views.BookView4.as_view())
  • views.py
"""基于GenericAPIView同级的9个视图类"""  # 继承了GenericAPIView+一个两个或三个视图扩展类,同时内部书写了相关的请求方法
from rest_framework.generics import CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView

class BookView4(ListAPIView, CreateAPIView):
    """观看源码,发现继承的类中已经有了相关的操作,做了更一层的覆盖"""
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers

class BookDetailView4(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers

5、基于ModelViewSet实现5个接口

到了这里,还没有完,我们应该想一下,如何将两个视图类,拼成一个视图类,我们发现最难的问题是,两个类中都有get方法,同时,路由配置也是一个问题,那么我们该如何解决呢?这就引入了ModelViewSet的使用。

  • urls.py
"""基于ModelViewSet实现5个接口"""
url(r'^books5/(?P<pk>\d+)', views.BookView5.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
# 当路径匹配,又是get请求,会执行BookView5的list方法
url(r'^books5/', views.BookView5.as_view(actions={'get':'list','post':'create'}))
  • views.py
"""基于ModelViewSet实现5个接口"""
from rest_framework.viewsets import ModelViewSet
class BookView5(ModelViewSet):  # 我们点击去发现,5个接口都有,也就是一个多继承而已,但是路由有点问题。
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers

我们如何理解上面的代码呢?

所以我们可以在views.py中更简单的进行书写。但是路由配置如何解决,我们再观察源码。

继承关系是ModelViewSet ----> GenericViewSet ----> ViewSetMixin,而在ViewSetMixin中重写了as_view()方法。我们进行进行查看。

由此,我们将路由进行actions参数的配置,即可解决路由冲突问题。

6、案例:继承ViewSetMixin的视图类

那么我们有了ViewSetMixin,我们可以延伸出什么东西呢?实际上,我们可以实现视图类不用get、post、delete、put...等作为类中的函数名,用自定义的函数名进行代替。

具体实现方式如下。

  • urls.py
# 基于ModelViewSet实现5个接口
url(r'^books6/', views.BookView6.as_view(actions={'get': 'get_all_book'}))
  • views.py
from rest_framework.viewsets import ViewSetMixin
# ViewSetMixin一定放在APIView之前,因为类的继承关系关系是深度优先算法,继承的两个类中均有as_view()方法。
class BookView6(ViewSetMixin, APIView):
    def get_all_book(self, request):
        book_list = models.Book.objects.all()
        book_ser = BookSerializers(book_list, many=True)
        return Response(book_ser.data)
posted @ 2021-07-21 08:33  YangYi215  阅读(146)  评论(0编辑  收藏  举报