zoukankan      html  css  js  c++  java
  • 安装与使用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)
    

    1574161117696

    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中取

    三、渲染模板

    根据用户请求的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',
        ],
       
    }
    
    

    1574164294714

    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())

    四、解析模块的使用

    服务器根据设置的请求头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'
        ]
    }
    
    

    1574167139160

    # 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.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)处理异常,就会走自己重写的异常函数,先交给系统处理(客户端的异常),系统没处理(服务器异常),再自己处理

    5.3.状态码响应模块

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

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

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

    3. 源码:Response类的__init__方法

    总结

    在当下的阶段,必将由程序员来主导,甚至比以往更甚。
  • 相关阅读:
    9.11 eventbus
    9.10,,,实现new instanceof apply call 高阶函数,偏函数,柯里化
    9.9 promise实现 写完了传到gitee上面了,这里这个不完整
    9.5cors配置代码
    9.5 jsonp 实现
    9.5 http tcp https总结
    9.3 es6 class一部分 and es5 class 发布订阅
    8.30 cookie session token jwt
    8.30vue响应式原理
    warning: LF will be replaced by CRLF in renard-wx/project.config.json. The file will have its original line endings in your working directory
  • 原文地址:https://www.cnblogs.com/randysun/p/12159493.html
Copyright © 2011-2022 走看看