zoukankan      html  css  js  c++  java
  • DRF之请求和响应

    DRF之请求和响应

    1 请求和响应

    1.1 请求

    rest_framework传入到视图中的request对象不再是Django默认的HttpResquest对象,而是rest_framework提供的扩展了HttpRequest类Request对象

    from rest_framework.request import Request
    
    # 对原生的request对象进行了二次封装
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
                .format(request.__class__.__module__, request.__class__.__name__)
        )
    
        self._request = request  #将原生的request对象赋值给了self._request
        
    # 利用断言来判断传入的request是否是`django.http.HttpResponse`的实例
    assert isinstance(request,HttpRequest),(异常信息)
    若assert 后面的条件成立,则什么都不执行;若不成立,则抛异常
    
    # 使用了代理模式
    def __getattr__(self, attr):
        # 如果Resquest对象没有attribute,我们将尝试代理底层的HttpResquest对象的属性
        try:
            return getattr(self._request, attr) # 利用反射来获取原生request对象的属性
        except AttributeError:
            return self.__getattribute__(attr)
        
    @property # 伪装属性  获取POST的请求提交的数据(包括urlencoded,formdata,json等编码格式的数据)
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data
    
    def _load_data_and_files(self):
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data
    
            if is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES
                
    
    # 获取get请求提交的数据
    @property
    def query_params(self):
        return self._request.GET
    

    REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。

    1.2 响应

    REST framework 提供了响应类Response,使用该类来构造响应对象的时候,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。

    from rest_framework.response import Response
    
    def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
        
    # data 你需要返回的数据,字典{‘msg’:'xxx','data':''}
    # status 返回的状态码,默认是200
    	--from rest_framework import status, 在这个路径下,涵盖了基本状态码,如:HTTP_200_OK = 200 HTTP_201_CREATED = 201
    # template_name 渲染的模板名称(自定义模板)
    # headers: 响应头,字典 headers:{'X-CSRFToken':'{{csrf-token}}'}
    # content_type: 响应编码的格式 [application/json] [text/html]
    

    REST framework提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。

    # 全局配置 在settings.py文件中配置REST FRAMEWORK
    REST_FRAMEWORK = {
        "DEFAULT_RENDERER_CLASSES":(
            'rest_framework.renderers.JSONRenderer', #json 格式渲染
            'rest_framework.renderers.BrowsableAPIRenderer', #渲染 api浏览器
        )
    }
    
    # 局部配置
    # 在视图类中做如下的调整:
    class Book1View(APIView):
        renderer_classes = [JSONRenderer, ]
    
        def get(self, request):
            pass
    
        def post(self, request):
            pass
        
    # DRF的配置信息:
     先从自己类(局部)中找,没有的话去全局(settings.py)中去找,再没有的就采用默认的
    

    2 视图(view)

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

    2.1 基于APIView来写接口

    APIViewrest_framework提供的所有视图的基类,它继承于DjangoView

    from rest_framework.views import APIView
    from rest_framework.renderers import JSONRenderer
    from rest_framework.response import Response
    
    from app01.models import BookModel
    from app01.serializer import BookSerializer
    
    class Book1View(APIView):
        renderer_classes = [JSONRenderer, ]
    
        def get(self, request):
            book_list = BookModel.objects.all()
            book_ser = BookSerializer(book_list, many=True)
            return Response(book_ser.data)
    
        def post(self, request):
            #  新增没有instance,修改才有instance
            book_ser = BookSerializer(data=request.data)
            print(request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status':101,'msg':'数据校验失败'})
    
    class Book1Detail(APIView):
        renderer_classes = [JSONRenderer, ]
    
        def get(self, request, pk):
            book = BookModel.objects.filter(pk=pk).first()
            if book:
                book_ser = BookSerializer(book)
                return Response(book_ser.data)
            else:
                return Response({'status': 101, 'msg': '无效的pk'})
    
        def put(self, request, pk):
            # 修改需要instance
            instance = BookModel.objects.get(pk=pk)
            book_ser = BookSerializer(instance=instance, data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status':101,'smg':'数据校验未通过'})
    
        def delete(self, request, pk):
            book = BookModel.objects.filter(pk=pk)
            if book:
                book.delete()
                return Response({'status':200,'msg':'删除成功'})
            else:
                return Response({'status':101,'msg':'无效的pk'})
    

    models.py

    class BookModel(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32,verbose_name='书名')
        price = models.DecimalField(max_digits=5,decimal_places=2,verbose_name='价格')
        author = models.CharField(max_length=32,verbose_name='作者')
        publish = models.CharField(max_length=32,verbose_name='出版社')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name_plural='图书表'
    

    serializer.py

    class BookSerializer(serializers.Serializer):
        book_id = serializers.CharField(source='id', read_only=True)
        book_name = serializers.CharField(source='name', max_length=32)
        book_price = serializers.CharField(source='price')
        author = serializers.CharField(max_length=32)
        publish = serializers.CharField(max_length=32)
    
        def create(self, validated_data):
            instance = BookModel.objects.create(**validated_data)
            return instance
    
        def update(self, instance, validated_data):
            for attr, value in validated_data.items():
                if hasattr(instance, attr):
                    setattr(instance, attr, value)
            instance.save()
            return instance
    

    我们发现基于APIView书写的视图比较繁琐,那么有没有简便的方法了,接下来,我们一起看下基于GenericAPIView书写的视图

    2.2 基于GenericAPIView来写接口

    GenericAPIView类继承于APIView 类,主要了增加了操作序列化器数据库查询的方法,为下面的Mixin扩展类的执行提供方法支持,可以搭配一个或多个Mixin扩展类

    class Book2View(GenericAPIView):
        renderer_classes = [JSONRenderer, ]
        # queryset参数需要传queryset对象,查询了所有的图书
        # serializer_class 使用序列化类来序列化这些数据
        queryset = BookModel.objects  # 类属性,对象调用,只能改自己的,不能改类属性的 
        serializer_class = BookSerializer  
    
        def get(self, request):
            book_list = self.get_queryset() # Book.objects.all()
            book_ser = self.get_serializer(book_list, many=True)
            return Response(book_ser.data)
    
        def post(self, request):
            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 Book2Detail(GenericAPIView):
        renderer_classes = [JSONRenderer, ]
    
        queryset = BookModel.objects
        serializer_class = BookSerializer
    
        def get(self, request, pk):
            book = self.get_object()
            book_ser = self.get_serializer(book)
            return Response(book_ser.data)
    
        def put(self, request, pk):
            book = self.get_object()
            book_ser = self.get_serializer(instance=book, 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):
            self.get_object().delete()
            return Response({'status': HTTP_200_OK, 'msg': '删除成功'})
    

    来看下GenericAPIView源码

    queryset = BookModel.objects
    serializer_class = BookSerializer
    其实,就是self.queryset和self.serializer_class
    # self是Book2View的实例化对象
    
    # book_list = self.get_queryset()
    def get_queryset(self):
        queryset = self.queryset  #BookModel.objects
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all() # BookModel.objects.all()
        return queryset
    
    # book_ser = self.get_serializer(book_list, many=True)
    # BookSerializer(book_list, many=True)
    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class() # BookSerializer
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs) # BookSerializer()
    
    def get_serializer_class(self):
        return self.serializer_class # BookSerializer
    
    # book=self.get_object()
    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())
        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field   #      self.lookup_field = 'pk' or self.lookup_url_kwarg = None   -->pk
    
    
        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs) # 这里帮你做了pk不存在的检测
    
        # May raise a permission denied
        self.check_object_permissions(self.request, obj) # 权限校验
    
        return obj # BookModel.objects.filter(pk=pk).first()
    

    GenericAPIView相较于APIView扩展了操作序列化器查询数据库的方法,但是相较于代码量并没有减轻多少,那么有没有更好的方法了。这就是下面5个视图扩展类的起源。

    2.3 基于GenericAPIView和5个视图扩展类来书写接口

    5个视图扩展类分别是:

    from rest_framework.mixins import ListModelMixin, CreateModelMixin   -->list(get all)  -->create(post)
    from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin  -->retrieve(get pk)
    -->update(put pk)   -->destroy(delete pk)
    
    class Book3View(GenericAPIView, ListModelMixin, CreateModelMixin):
        renderer_classes = [JSONRenderer, ]
        queryset = BookModel.objects
        serializer_class = BookSerializer
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    class Book3Detail(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        renderer_classes = [JSONRenderer, ]
        queryset = BookModel.objects
        serializer_class = BookSerializer
    
        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个操作,我们需要写两个类图类,因为get(有查询多个,有查询单个),我们把(get,post,put,delete) 分为两类:

    (1)不需要传入主键(pk)
    <1>get获取所有的数据;
    <2>post新增一条数据
    (2)需要传入主键(pk)
    <1>get获取单个数据
    <2>put更新一条数据
    <3>delete删除一条数据
    

    那么,有没有可以把两个视图类整合的方案了。这里,就要用到了ModelViewSet

    2.4 使用ModelViewSet书写5个接口

    from rest_framework.viewsets import ModelViewSet
    
    class Book4View(ModelViewSet):
        renderer_classes = [JSONRenderer, ]
        queryset = BookModel.objects
        serializer_class = BookSerializer
    

    urls.py

        # 使用ModelViewSet来写5个接口
        path('books4/', views.Book4View.as_view(actions={'get': 'list', 'post': 'create'})),
        path('books4/<int:pk>/', views.Book4View.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
    

    2.5 源码分析ViewSetMixin

    # ViewSetMixin 重写了as_view()方法
    # 核心代码
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)   # book对象
        self.action_map = actions  # {’get':'list','post':'create'}
    
        # Bind methods to actions
        # This is the bit that's different to a standard view
        for method, action in actions.items():
            # exp: action=list method=get
            handler = getattr(self, action) # 利用反射,获取list的内存地址地址
            setattr(self, method, handler) # 将list的内存地址赋值给get,以后执行slef.get(),就相当于执行了slef.list()
    
    

    2.6 继承于ViewSetMixin类的视图类

    class Book5View(ViewSetMixin, APIView):
        def get_book_list(self, request):
            book_list = BookModel.objects.all()
            book_ser = BookSerializer(book_list, many=True)
            return Response(book_ser.data)
    
        def add_one_book(self, request):
            book_ser = BookSerializer(data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status': 101, 'msg': '数据未通过'})
    
        def get_one_book(self, request, pk):
            book = BookModel.objects.filter(pk=pk).first()
            if book:
                book_ser = BookSerializer(book)
                return Response(book_ser.data)
            else:
                return Response({'status': 101, 'msg': '无效的pk'})
    
        def update_one_book(self, request, pk):
            instance = BookModel.objects.filter(pk=pk).first()
            book_ser = BookSerializer(instance=instance, data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status': 101, 'msg': '数据未通过校验'})
    
        def delete_one_book(self, request, pk):
            BookModel.objects.filter(pk=pk).delete()
            return Response({'status': HTTP_200_OK, 'msg': '成功删除'})
    

    来个简单粗暴版本!

    class Book6View(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,
                    DestroyModelMixin):
        renderer_classes = [JSONRenderer, ]
        queryset = BookModel.objects
        serializer_class = BookSerializer
    
        def get_book_list(self, request):
            return self.list(request)
    
        def add_one_book(self, request):
            return self.create(request)
    
        def get_one_book(self, request, pk):
            return self.retrieve(request, pk)
    
        def update_one_book(self, request, pk):
            return self.update(request, pk)
    
        def delete_one_book(self, request, pk):
            return self.destroy(request, pk)
    

    3 各个类的继承关系

    • APIVIew
    APIView--->View--->object
    Book1View--->APIView--->View--->object
    Book1Detail--->APIView--->View--->object
    

    • GenericAPIView
    GenericAPIView--->APIView--->View--->object
    Book2View--->GenericAPIView--->APIView--->View--->object
    Book2Detail--->GenericAPIView--->APIView--->View--->object
    

    • GenericAPIView,ListModelMixin, CreateModelMixin
    Book3View ---> GeneriCAPIView --->APIView --->View --->object
              ---> ListModelMixin                      --->object
        	  ---> CreateModelMixin                    --->object
    

    ​ (GenericAPIView,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin)

    Book3Detail ---> GeneriCAPIView --->APIView --->View --->object
                ---> RetrieveModelMixin                  --->object
        	    ---> UpdateModelMixin                    --->object
            	---> DestroyModelMixin                   --->object
    

    • ModelViewSet
    Book4View-->ModelViewSet-->GenericViewSet-->GenericAPIView-->APIView-->View-->object
                                             -->ViewSetMixin                   -->object
                            -->ListModelMixin                                  -->object
            				-->CreateModelMixin                                -->object
                			-->RetrieveModelMixin                              -->object
        	                -->UpdateModelMixin                                -->object
            	            -->DestroyModelMixin                               -->object
    

    • (ViewSetMixin,APIView)
    Book5View--->ViewSetMixin   --->object
             --->APIView--->View--->object  
    

    • (ViewSetMixin, GenericAPIView,ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin)
    Book6View--->ViewSetMixin                     --->object
    		 --->GenericAPIView--->APIView--->View--->object
        	 --->ListModelMixin                   --->object
             --->CreateModelMixin                 --->object
             --->RetrieveModelMixin               --->object
             --->UpdateModelMixin                 --->object
             --->DestroyModelMixin                --->object
    

  • 相关阅读:
    centos7.6安装Oracle11g过程记录(下)
    centos7.6 安装解压缩软件
    centos7.6 安装及配置ftp服务
    MySQL8.0的主从配置过程记录
    解决 /dev/mapper/centos-root 空间不足的问题
    ASP判断当前页面上是否有参数ID传递过来
    通过ASP禁止指定IP和只允许指定IP访问网站的代码
    asp自动补全html标签自动闭合(正则表达式)
    asp中utf8不会出现乱码的写法
    通过安全字符串过滤非法字符
  • 原文地址:https://www.cnblogs.com/surpass123/p/13269267.html
Copyright © 2011-2022 走看看