Django之CBV中View、APIView以及drf的Request源码分析

View源码分析:

views文件:

class MyView(View):
    def get(self,request):
        return HttpResponse(ok)
    def post(self,request):
        return HttpResponse(post)

urls文件:

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

View源码思路分析(这里拿重要的部分源码进行分析):

‘‘‘
主要思路:View--》as_view --》as_view下的函数
当有路由匹配进来的时候,就会执行as_view这个方法
‘‘‘

class View(object):
    http_method_names = [get, post, put, patch, delete, head, options, trace]  
    #继承classmethod
    @classonlymethod
    ‘‘‘
      这时会把我们在vies创建的MyView类,当作参数传递给as_view这个方法。
      相当于as_view(MyView)
      **initkwargs是url匹配传递过来的参数:例如 url(r‘^$‘,views.MyView.as_view(age=1)),
    ‘‘‘
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """

        def view(request, *args, **kwargs):
            ‘‘‘
            self = cls(**initkwargs)
            这一步是实例化一个对象,将我们传进来的类实例化赋值给self
            相当于self=MyView(参数)
            ‘‘‘
            self = cls(**initkwargs)

            if hasattr(self, get) and not hasattr(self, head):
                self.head = self.get
            self.request = request  #将当前的request请求赋值给对象中的request.
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        return view

    def dispatch(self, request, *args, **kwargs):
        # request: 还是当前的请求对象
        if request.method.lower() in self.http_method_names:
            ‘‘‘
            获取当前对象(MyView类的对象)的方法的内存地址
            例如get请求:handler=getattr(self,‘get‘)
            ‘‘‘
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs) #相当于执行get(request)

APIView源码分析:

 views文件:

from rest_framework.views import APIView
class MyAPIView(APIView):
    def get(self,request):
        #此时的request是drf重新封装后的request
        print(request._request)  #原生request
        # 相当于request.GET
        data=request.query_params  
        return HttpResponse(ok)
    def post(self,request):
        # 相当于request.POST
        post_data=request.data
        file_data=request.FILES

urls文件

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

APIView源码分析:

‘‘‘
#请求来了-》路由匹配上-》view(request)-》调用了self.dispatch(),这时执行的是apiview的dispatch,而不是view的dispatch
‘‘‘
class APIView(View):

    @classmethod
    def as_view(cls, **initkwargs):
        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)
        此时的view就View源码的as_view方法下的view
        即调用父类(view)的as_view方法。
        此时调用的dispatch方法是APIView的dispatch方法,而不是View的
        执行顺序是从当前类(MyAPIView)中查找,找不到再从父类(APIView)中查找,再找不到再从APIView的父类View中找
        ‘‘‘
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        #只要继承了APIView,就不会再进行csrf认证
        return csrf_exempt(view)

    #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
            左边的request是重新封装后的request
            具体可以参照源码4
        ‘‘‘
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #三大认证模块(具体看下面源码5)
            self.initial(request, *args, **kwargs)
            # 这部分跟View类中那部分一样。
            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、重新封装request的方法
    def initialize_request(self, request, *args, **kwargs):
        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 APIView的initial方法 def initial(self, request, *args, **kwargs): # 做版本的控制 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted ‘‘‘ 认证组件:校验用户 - 游客、合法用户、非法用户 游客:代表校验通过,直接进入下一步校验(权限校验) 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验) 非法用户:代表校验失败,抛出异常,返回403权限异常结果 ‘‘‘ self.perform_authentication(request) ‘‘‘ 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色 认证通过:可以进入下一步校验(频率认证) 认证失败:抛出异常,返回403权限异常结果 ‘‘‘ self.check_permissions(request) ‘‘‘ 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s) 没有达到限次:正常访问接口 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问 ‘‘‘ self.check_throttles(request)

 rest_framework下的Request类源码分析:

from rest_framework.request import Request
class 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

    def __getattr__(self, attr):
        """
        通过反射,将原生request对象,以及属性和方法取出
        """
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

    ‘‘‘
    通过@property装饰器将data方法封装成属性
    data方法:它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
    ‘‘‘
    @property
    def data(self):
        if not _hasattr(self, _full_data):
            self._load_data_and_files()
        return self._full_data
    ‘‘‘
    get请求的数据都在这里取
    ‘‘‘
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
  ‘‘‘
     存文件数据
    ‘‘‘ 
    @property
    def FILES(self):
        if not _hasattr(self, _files):
            self._load_data_and_files()
        return self._files

 

Django之CBV中View、APIView以及drf的Request源码分析

上一篇:JS Window 对象您知道多少?


下一篇:c# 测试对比几种对比空串的写法性能