运维开发网

DRF框架 生命周期 及源码分析

运维开发网 https://www.qedev.com 2020-07-25 10:00 出处:网络 作者:运维开发网整理
安装 pip3 install djangorestframework   drf请求生命周期 准备前戏: 视图层 views.py from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response # Create

安装

pip3 install djangorestframework

 

drf请求生命周期

准备前戏:

视图层 views.py

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
# Create your views here.

class test(APIView):
    def get(self,reqeust,*args,**kwargs):
        return Response(‘drf get ok‘)
    def post(self,request,*args,**kwargs):
        return Response(‘drf post ok‘)

路由层 urls.py

from django.conf.urls import url,include
from django.contrib import admin

from . import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^test/‘, views.test.as_view()),
]

 

1.此处的 views.test.as_view()   先从对象中找as_view()方法,没有再从自定义test类中找,还没有因此需要去父类APIView中找

最终找到如下,rest_framework\views.py:

lass APIView(View):

    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # Allow dependency injection of other settings to make testing easier.
    settings = api_settings

    schema = DefaultSchema()

    @classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, ‘queryset‘, None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    ‘Do not evaluate the `.queryset` attribute directly, ‘
                    ‘as the result will be cached and reused between requests. ‘
                    ‘Use `.all()` or call `.get_queryset()` instead.‘
                )
            cls.queryset._fetch_all = force_evaluation

        view = super().as_view(**initkwargs)   #调用父类的as_view  也就是django原生的base.py中的as_view
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.

        #继承APIView的视图类会禁用csrf认证
        return csrf_exempt(view)

 

2.view = super().as_view(**initkwargs)   调用了父类的方法  也就是django原生的 base.py中的 as_view方法

 之后再此基础上增加了  一句  return csrf_exempt(view)  这是继承了APIView的视图类   会禁用csrf认证  这样就不需要再settings中注释掉了

3. 在第二步时 调用父类的as_view()方法,此方法中有个关键的  分发方法  return self.dispatch(request, *args, **kwargs)

下面是原生django  base.py as_view方法

 @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don‘t do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)   #实例化产生对象
            if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            # 上面几句话都仅仅给对象新增属性
            return self.dispatch(request, *args, **kwargs)#重点 分发   得到 return handler(request,*args,**Kwargs)
            # 实际上就是执行我们自己类里面的请求方法得到的结果返回给前台
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

 

4.APIView中重写了dispatch方法  

 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)
        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
            #第三步:执行:get/post/put/delete函数
            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

 

5.完成任务方法交给视图类的请求函数处理,得到请求的响应结构,返回给前台

 

请求模块:request对象

源码入口

APIView类的dispatch方法中:request = self.initialize_request(request, *args, **kwargs)

源码分析

"""
# 二次封装得到def的request对象
request = self.initialize_request(request, *args, **kwargs) 点进去

# 在rest_framework.request.Request实例化方法中
self._request = request  将原生request作为新request的_request属性

# 在rest_framework.request.Request的__getattr__方法中
try:
    return getattr(self._request, attr)  # 访问属性完全兼容原生request
except AttributeError:
    return self.__getattribute__(attr)
"""

 

重点总结

# 1) drf 对原生request做了二次封装,request._request就是原生request
# 2) 原生request对象的属性和方法都可以被drf的request对象直接访问(兼容)
# 3) drf请求的所有url拼接参数均被解析到query_params中,所有数据包数据都被解析到data中
lass Test(APIView):
    def get(self, request, *args, **kwargs):
        # url拼接的参数
        print(request._request.GET)  # 二次封装方式
        print(request.GET) # 兼容
        print(request.query_params) # 拓展

        return Response(‘drf get ok‘)

    def post(self, request, *args, **kwargs):
        # 所有请求方式携带的数据包
        print(request._request.POST)  # 二次封装方式
        print(request.POST)  # 兼容
        print(request.data)  # 拓展,兼容性最强,三种数据方式都可以

        print(request.query_params)

        return Response(‘drf post ok‘)

 

 

渲染模块:浏览器和Postman请求结果渲染数据的方式不一样

源码入口

APIView类的dispatch方法中:self.response = self.finalize_response(request, response, *args, **kwargs)

源码分析

"""
# 最后解析reponse对象数据
self.response = self.finalize_response(request, response, *args, **kwargs) 点进去

# 拿到运行的解析类的对象们
neg = self.perform_content_negotiation(request, force=True) 点进去

# 获得解析类对象
renderers = self.get_renderers() 点进去

# 从视图类中得到renderer_classes请求类,如何实例化一个个对象形参解析类对象列表
return [renderer() for renderer in self.renderer_classes]


# 重点:self.renderer_classes获取renderer_classes的顺序
#    自己视图类的类属性(局部配置) => 
#    APIView类的类属性设置 => 
#    自己配置文件的DEFAULT_RENDERER_CLASSES(全局配置) => 
#    drf配置文件的DEFAULT_RENDERER_CLASSES
"""

 

全局配置:所有视图类统一处理,在项目的settings.py中

REST_FRAMEWORK = {
    # drf提供的渲染类
    ‘DEFAULT_RENDERER_CLASSES‘: [
        ‘rest_framework.renderers.JSONRenderer‘,
        ‘rest_framework.renderers.BrowsableAPIRenderer‘,
    ],
}

 

局部配置:某一个或一些实体类单独处理,在views.py视图类中提供对应的类属性

class Test(APIView):
    def get(self, request, *args, **kwargs):
        return Response(‘drf get ok‘)

    def post(self, request, *args, **kwargs):
        return Response(‘drf post ok‘)

# 在setting.py中配置REST_FRAMEWORK,完成的是全局配置,所有接口统一处理
# 如果只有部分接口特殊化,可以完成 - 局部配置
from rest_framework.renderers import JSONRenderer
class Test2(APIView):
    # 局部配置
    renderer_classes = [JSONRenderer]
    def get(self, request, *args, **kwargs):
        return Response(‘drf get ok 2‘)

    def post(self, request, *args, **kwargs):
        return Response(‘drf post ok 2‘)

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号