• django-restframework


    django-restframework

    一、安装与使用

    1.安装

    >: pip3 install djangorestframework
    

    2.使用

    • 在settings.py中注册:

      INSTALLED_APPS = [   
          ....
          'api.apps.ApiConfig',
          # drf必须注册
          'rest_framework',
      ]
      
    • 模块

      # drf的封装风格
      from rest_framework.views import APIView  # CBV
      from rest_framework.response import Response # 响应
      from rest_framework.request import Request # 请求
      from rest_framework.filters import SearchFilter # 过滤器
      from rest_framework.pagination import PageNumberPagination # 分页
      from rest_framework.exceptions import APIException # 异常
      from rest_framework.authentication import BaseAuthentication # 验证
      from rest_framework.permissions import IsAuthenticated # 组件
      from rest_framework.throttling import SimpleRateThrottle # 
      from rest_framework.settings import APISettings  # rest_framework配置文件
      from rest_framework import status # 状态码
      
      from django.http.request import QueryDict  # 类型
      
    • 基于CBV完成满足RSSTful规范接口

    二、request请求分析

    2.1. request数据请求

    # views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django.http.request import QueryDict
    class BookAPIView(APIView):
    
        def get(self, request, *args, **kwargs):
            print(request._request.GET)  # 原生wigs中值
            print(request.GET) # 二次封装的值
            print(request.META) # 所有get请求信息
            print(request.META.get("HTTP_AUTH")) # 前台发来的头都会大写并加上HTTP
            print(request.query_params) # 所有url数据
            
            print(request.POST) # get请求不能获取Post请求数据
    
            return Response('get ok')
    
        def post(self, request, *args, **kwargs):
            print(request._request.POST)  # 原生wigs中值
            print(request.POST) # 二次封装的值
            print(request.data) # 所有post请求数据
            print(request._request) # url数据包<WSGIRequest: POST '/books/?age=123'>
            print(request.GET) # POST请求可以Post请求数据
                
             # QueryDict转为
            print(request.query_params.dict())
            if isinstance(request.data, QueryDict):
                print(request.data.dict())
    
            return Response('post ok')
        
        
        
    # urls.py
    from . import views
    urlpatterns = [
        url(r'^books/$', views.BookAPIView.as_view()),
    ]
    
    

    img

    总结:

    1. drf中的request是在wsgi的request基础上进行再一次封装
    2. 将wsgi的request作为drf的request的一个属性, _request
    3. drf中的request对wsgi中的request做完全兼容,新的可以直接获取wsgi中的数据
    4. drf中的request对数据解析更规范化,所有的拼接参数(url?age=18)都解析到了query_params中,所有数据报数据都解析到data中
    5. query_params和data属于QueryDict类型,可以通过.dict()转化为字典类型

    2.2 request请求源码分析

    # 1. 2. urls.py
    from . import views
    urlpatterns = [
        url(r'^books/$', views.BookAPIView.as_view()),
    ]
    
    #3.调用drf中的as_view
    @classmethod
    def as_view(cls, **initkwargs):     # cls 为 views.BookAPIView类
        ...
        view = super().as_view(**initkwargs) # as_view原生的as_view,super() 为 view
        view.cls = cls
        view.initkwargs = initkwargs
    
        return csrf_exempt(view)
    # 4.进入dif中dispatch 
    def dispatch(self, request, *args, **kwargs):   # self为views.BookAPIView类
        self.args = args  
        self.kwargs = kwargs
        # 对原生request进行了二次封装
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
        try:
            self.initial(request, *args, **kwargs)
    
            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                    response = handler(request, *args, **kwargs)
    
                    except Exception as exc:
                        response = self.handle_exception(exc)
    
                        self.response = self.finalize_response(request, response, *args, **kwargs)
                        return self.response
                    
    # 4.1 返回封装的request对象
    def initialize_request(self, request, *args, **kwargs):
        """
            Returns the initial request object.
            """
        parser_context = self.get_parser_context(request) # 解析
    
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    # 5.Request类创建 self._request = request自己的属性
     def __init__(self, request, parsers=None, authenticators=None,
                     negotiator=None, parser_context=None):
            assert isinstance(request, HttpRequest), (
               
                .format(request.__class__.__module__, request.__class__.__name__)
            )
    
            self._request = request
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()
            self.negotiator = negotiator or self._default_negotiator()
            self.parser_context = parser_context
            
    #6.如何实现获取wgis中的属性
    def __getattr__(self, attr):   
        """
            If an attribute does not exist on this instance, then we also attempt
            to proxy it to the underlying HttpRequest object.
            """
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
    

    img

    1. 导入drf中的APIView并创建类继承它,在配置url: url(r'^books/$', views.BookAPIView.as_view()),
    2. 当项目启动时会执行drf中的as_view()方法,返回view方法(局部禁用了csrf认证), url:url(r'^books/$', views.BookAPIView.view
    3. 浏览器发送请求来时则会执行url中view函数,在执行view中返回的是 self.dispatch(request, *args, **kwargs), dispatch是drf中的dispatch, 不是wsgi中原生的dispatch
    4. 进入drf中的dispatch首先会对原生的request进行二次封装:request = self.initialize_request(request, *args, **kwargs),在initialize_request方法中返回了一个request实例对象
    5. 进入request.py中的Request方法中就会发现在初始化方法中将wsgi方法的request变为了自己的一个属性: 'self._request = request'
    6. request通过反射的方法获取wsgi中request属性,当request.获取属性先到 _request中查找,没有则查自己本身的
    7. 核心
      • 走drf的Request初始化方法__init__:self._request = request
      • drf的Request的getter方法__getattr__:先从self._request反射取属性,没取到再从drf的request中取
      • request除了可以访问原wsgi协议的request所有内容,还可以访问 query_params、data

    三、渲染模板

    根据用户请求的RUL向服务器要响应的数据类型,比如:json数据,xml数据,将这些数据向用户返回

    3.1渲染模板的使用

    # 在renderers.py模板模块中导入你要渲染的模板
    # JSONRenderer: 返回json数据
    # BrowsableAPIRenderer: 返回浏览器html页面
    
    from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
    
    # 1.局部配置
    # 渲染模块的局部配置
    # 局部禁用就是配置空list:[]  # pytthon3.7 有问题
    renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
    
    # 2.全局配置
    # drf的配置,在drf中settings.py查看如何配置
    REST_FRAMEWORK = {
        # 渲染模块的全局配置:开发一般只配置json
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.TemplateHTMLRenderer',
        ],
       
    }
    
    

    img

    3.2渲染模板的源码解析

    # 1 对请求响应进行二次封装
    def dispatch(self, request, *args, **kwargs):
        ....
        ....
        # 二次处理响应
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    
    # 2. 接续配置的渲染类
    def finalize_response(self, request, response, *args, **kwargs):
    
        if isinstance(response, Response):
            if not getattr(request, 'accepted_renderer', None):
                # 点进去,内部解析了配置的渲染的类
                neg = self.perform_content_negotiation(request, force=True)
                # 拿到渲染类对象
                request.accepted_renderer, request.accepted_media_type = neg
                
                response.accepted_renderer = request.accepted_renderer
                response.accepted_media_type = request.accepted_media_type
                response.renderer_context = self.get_renderer_context()
    
    # 3. 获取渲染对象的类    
    def perform_content_negotiation(self, request, force=False):
        """
            Determine which renderer and media type to use render the response.
            """
        # 点进去,获取配置的渲染类
        renderers = self.get_renderers()
        conneg = self.get_content_negotiator()
        try:
            return conneg.select_renderer(request, renderers, self.format_kwarg)
        except Exception:
            if force:
                return (renderers[0], renderers[0].media_type)
            raise
    
    # 3 renderer_classes 自己全局
     def get_renderers(self):
            """
            Instantiates and returns the list of renderers that this view can use.
            """
            # 从自己的视图类找renderer_classer类属性(局部配置)APIView的类属性(从自己配置文件中找)> 自己的项目配置文件中找(全局配置), drf默认配置问价中找(默认配置)
            return [renderer() for renderer in self.renderer_classes]
        
    class BaseRenderer:
        """
        All renderers should extend this class, setting the `media_type`
        and `format` attributes, and override the `.render()` method.
        """
        media_type = None
        format = None
        charset = 'utf-8'
        render_style = 'text'
    
        def render(self, data, accepted_media_type=None, renderer_context=None):
            raise NotImplementedError('Renderer class requires .render() to be implemented')
    
    
    

    总结:

    1. 根据流程,再次进入drf的dispatch方法,该方法对响应数据进行了二次封装,然后,进入self.finalize_response方法
    2. 在self.finalize_respons方法中perform_content_negotiation中获取渲染的对象结果,进入perform_content_negotiation方法
    3. perform_content_negotiation方法中实现了获取配置文件的渲染结果集,通过self.get_renderers()方法
    4. 在self.get_renderers()方法中self.renderer_classes读取配置的渲染对象,先从创建的视图类中(BookAPIView(APIView)(局部配置))找,然后在从自己的项目文件中找(项目中 settings.py,全局配置), 最后从drf默认配置文件中(默认配置)
    5. renderer()是从配置文件中获取到的对应类然后到 renderer.py 文件中获取类对象,创建的类对象(JSONRenderer())返回数据渲染的结果
    6. 核心:可以全局和局部配置视图类支持的结果渲染:默认可以json和页面渲染,学习该模块的目的是开发可以全局只配置json方式渲染

    四、解析模块的使用

    服务器根据设置的请求头content-type接收客户端对应的数据信息

    # JSONParser: 只接收json数据
    # FormParser: 只接收urlencoded
    # MultiPartParser:只接收form-data
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    # 1.局部配置
    # 解析模块的局部配置
    #局部配置禁用就是配置空list[]
    parser_classes = [JSONParser, MultiPartParser, FormParser]
    
    # 2.全局配置
    # drf的配置,在drf中settings.py查看如何配置
    REST_FRAMEWORK = {   
        # 解析模块的全局配置
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            'rest_framework.parsers.FormParser',
            'rest_framework.parsers.MultiPartParser'
        ]
    }
    
    

    img

    # 1.对数据进行二次解析
    def dispatch(self, request, *args, **kwargs):
        """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
        self.args = args
        self.kwargs = kwargs
        # 对原生request进行了二次封装,查看解析
        request = self.initialize_request(request, *args, **kwargs)
        ...
        
    # 2 获取解析数据
    def initialize_request(self, request, *args, **kwargs):
        """
            Returns the initial request object.
            """
        # 准备要解析的字典
        parser_context = self.get_parser_context(request)
    
        return Request(
            # 初始wigs中request
            request, 
            # 解析模块在封装原生request是,将数据一并解析
            parsers=self.get_parsers(), 
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context # 解析的内容
        )
        
    # 2.1 返回要解析内容字典
    def get_parser_context(self, http_request):
        """
            Returns a dict that is passed through to Parser.parse(),
            as the `parser_context` keyword argument.
            """
        # Note: Additionally `request` and `encoding` will also be added
        #       to the context by the Request object.
        return {
            'view': self,
            'args': getattr(self, 'args', ()),
            'kwargs': getattr(self, 'kwargs', {})
        }
    
    # 3 获取要解析的对象
    def get_parsers(self):
        """
         Instantiates and returns the list of parsers that this view can use.
         """
        return [parser() for parser in self.parser_classes]
    
    class BaseParser:  
        media_type = None
        def parse(self, stream, media_type=None, parser_context=None):      
            raise NotImplementedError(".parse() must be overridden.")
    

    总结:

    1. 根据流程,再次进入drf的dispatch方法,该方法对响应数据进行了二次封装同时也对数据进行了二次解析,然后,进入 request = self.initialize_request(request, *args, **kwargs)方法
    2. 在 self.initialize_request方法中通过self.get_parser_context(request)方法,获取要解析的数据,get_parser_context(self, http_request):方法返回解析的数据字典返回
    3. 在Request类中调用get_parsers(self)方法获取解析的对象,解析的随想先从创建的视图类中(BookAPIView(APIView)(局部配置))找,然后在从自己的项目文件中找(项目中 settings.py,全局配置), 最后从drf默认配置文件中(默认配置),实现数据的解析
    4. 只有在后台设置了解析的数据,才会给前台正确的响应否则报错
    5. 核心:请求的数据包格式会有三种(json、urlencoded、form-data),drf默认支持三种数据的解析,可以全局或局部配置视图类具体支持的解析方式

    五、异常模块的使用

    重写异常模块的目的是记录异常信息(日志记录)

    5.1异常模块的使用

    # 1. 配置异常模块,通过settings.py获取
    REST_FRAMEWORK = {  
        # 异常模块配置
        # Exception handling
        # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', # 默认
        'EXCEPTION_HANDLER': 'api.utils.exception_handler', # 自己重写的路径
        'NON_FIELD_ERRORS_KEY': 'non_field_errors',
    
    }
    
    # 2.编写异常模块,创建一个新的文件.py重写exception_handler方法
    from rest_framework.response import Response
    def exception_handler(exc, context):
        # 在exception_handler函数完成异常信息的返回以及异常信息的logging日志
        print(exc)
        print(context)
        print(type(exc))
        # exc.detail 携带错误的信息
        return Response(f'{context["view"].__class__.__name__}{exc}')
    
    # 2.2升级版
    from rest_framework.response import Response
    from rest_framework.views import exception_handler as drf_exception_handler
    
    def exception_handler(exc, context):
        response = drf_exception_handler(exc, context)
        if response is None:  # drf没有处理的异常
    
            response = Response({"detail": f'{context["view"].__class__.__name__}{exc}'})
        # 项目阶段,将日志记录保存到文件中
        return response
    
    # 2.3终极版
    from rest_framework.response import Response
    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework import status
    
    
    def exception_handler(exc, context):
        response = drf_exception_handler(exc, context)
        if response is None:  # drf没有处理的异常
    
            response = Response(data={
                'status': 7,
                "detail": f'{context["view"].__class__.__name__}{exc}',
            },
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        response = Response(status=response.status_code, data={
            'status': 7,
            "detail": f'{response.data.get("detail")}',
        })
        # 项目阶段,将日志记录保存到文件中
        return response
    
    

    5.2异常源码分析

     # 1. 异常出错
        def dispatch(self, request, *args, **kwargs):      
            try:
                self.initial(request, *args, **kwargs)
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                # 异常报错
                response = self.handle_exception(exc)
            # 二次处理响应
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    
    # 2 异常处理
    def handle_exception(self, exc):
        """
            Handle any exception that occurs, by returning an appropriate response,
            or re-raising the error.
            """
        if isinstance(exc, (exceptions.NotAuthenticated,
                            exceptions.AuthenticationFailed)):
            # WWW-Authenticate header for 401 responses, else coerce to 403
            auth_header = self.get_authenticate_header(self.request)
    
            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN
            # 都会走,获取异常处理函数
            exception_handler = self.get_exception_handler()
            # 获取请求数据内容字典
            context = self.get_exception_handler_context()
            # 异常函数:接收exc,context,返回response对象
            response = exception_handler(exc, context)
    
            if response is None:
                # 异常函数如果为空交给原生django处理
                self.raise_uncaught_exception(exc)
                # 异常返回
                response.exception = True
                return response
        # 2.1获取异常处理函数具体函数,可以自己配置
     def get_exception_handler(self):
            """
            Returns the exception handler that this view uses.
            """
            return self.settings.EXCEPTION_HANDLER
    

    总结:

    1. 在APIView的dispatch方法中,有一个try...except...异常,将代码运行的异常都交给异常处理模块进行处理: response = self.handle_exception(exc)

    2. 在handle_exception()中从配置中映射处理异常的函数(自定义异常模块就是自定义配置函数指向自己的函数),通过self.get_exception_handler()去获取配置文件中异常函数对象,然后将数据给自己写的函数进行处理,如果没有配置则是框架中默认的

    3. 异常函数exception_handler(exe,content)处理异常,就会走自己重写的异常函数,先交给系统处理(客户端的异常),系统没处理(服务器异常),再自己处理

    4. 核心:异常信息都需要被logging记录,所以需要自定义;drf只处理客户端异常,服务器异常需要手动处理,统一处理结果

    5.3.状态码响应模块

    class APIResponse(Response):
        # 格式化data
        def __init__(self, status=0, msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
            data = {  # json的response基础有数据状态码和数据状态信息
                'status': status,
                'msg': msg
            }
            if results is not None:  # 后台有数据,响应数据
                data['results'] = results
            data.update(**kwargs)  # 后台的一切自定义响应数据直接放到响应数据data中
            super().__init__(data=data, status=http_status, headers=headers, exception=exception)
    
    

    Response类生成对象需要的参数,以及Response类的对象可以使用的属性

    1. 参数:Response(data=响应的数据, status=响应的网络状态码, headers=想通过响应头再携带部分信息给前端)

    2. 属性:response.data response.status_code response.status_text

    3. 源码:Response类的__init__方法

    4. 核心:知道response对象产生可以传那些信息,response对象又是如何访问这些信息的

    六、总结

    在当下的阶段,必将由程序员来主导,甚至比以往更甚。
  • 相关阅读:
    触摸屏测试:Tslib
    Ubuntu:我不小心把/var/lock文件夹给删了
    驱动开发学习笔记. 0.02 基于EASYARM-IMX283 烧写uboot和linux系统
    驱动开发学习笔记. 0.01 配置arm-linux-gcc 交叉编译器
    驱动开发学习笔记.0.00 从拿到一块开发板开始
    利用联合体通过串口收发浮点数
    stm32 MDK5软件仿真之查看io口输出
    ListView 类
    INotifyPropertyChanged 接口
    ItemsControl 类绑定数据库
  • 原文地址:https://www.cnblogs.com/randysun/p/12152782.html
Copyright © 2020-2023  润新知