Django 之restfromwork 源码---APIView 分析

Django 之 djangorestframework的APIView分析

APIView 类中的as_view() 方法

首先 我们从视图函数入手,在urls.py 中的 URLconfig中添加一条路由

url(r'^books/',views.Book.as_view()),

此时,我们的BookView已经不是继承自django.views中View了,而是restframework.views中的APIView

from django.shortcuts import render,HttpResponse
#导入APIView
from rest_framework.views import APIView

#继承自APIView
class Book(APIView):
    # 这里的request 是被封装后的request,原生的request在request._request
    # 如果想用原生request中的属性, 还是原来的用法,因为Request重写了__getattr__方法。
    # 原生django只能处理 urlencoded 和formdata编码。
    # 如果是json格式,原生django是不能处理的,需要自己从body中取出来自行处理。
    # request.data  不管前端传数据的格式是  urlencoded 和formdata编码 还是json 都从里面取值。
    # request.query_params  里面存的是原生Django 中 GET 方法提供的值

    # self.FILES  就是上传的文件
    def get(self,request):
        data = request.data

        print(data)
        return HttpResponse('get....')

    def post(self,request):
        return HttpResponse('post....')

as_view():由于BookView没有实现as_view()方法,django启动时,调用的as_view()是APIView中的as_view()

as_view() 方法

# APIView 类中的 as_view() 方法

@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.
        这允许我们在进行URL反向查找时发现关于视图的信息
        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
        # APIView 是继承自django.views中的View,因此这里的 as_view() 也是调用父类的as_view()
        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)

源码分析:

? 继承了APIView之后:

  • ? 所有的请求都没有csrf的认证了
  • ? 在APIView中 as_view本质还是调用父类(View)中的as_view()
  • ? as_view 中调用dispatch ————> 这个dispatch 是 APIView的dispatch

dispatch() 方法

  • 对原生的request对象做了一层包装(面向对象的封装),以后再用的request 对象都是新的的request对象
  • 在 APIView 中 self.initial(request,*args,**kwargs) 里面有频率控制,权限控制和认证相关
  • 根据请求方法执行咱们写的视图类中的相应方法:
    • 视图类中方法的request对象,已经变成 了封装后的request
    • 在Request类中:
      • 原生的request是self._request
      • 取以post形式提交数据,从reuqest.data中取
      • query_params 就是原生request 的GET 数据
      • 上传文件时从FILES中取
      • (重要)其他的属性,自己request.属性名(因为重写了__getattr__方法)
# dispatch
def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,  该方法很想Django 原先的dispatch方法
        but with extra hooks for startup, finalize, and exception handling. 但是它扩展了一些用于启动,结束,异常处理的钩子
        """
        self.args = args  # 这里的self 对于上述的Book类而言是一个Book类的对象
        self.kwargs = kwargs
        # 将原先的request 封装后返回
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request  # 将 上面返回的值在request接收后,又放在self对象的自己的request中去
        self.headers = self.default_response_headers  # deprecate?

        try:
            # 该处用的request 就是被封装过后的request,原先的request 在 rerquest._request中
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            # 如果request请求方法在在 http_method_names列表中的方法中
            if request.method.lower() in self.http_method_names:
                # getattr 反射去获取该方法的内存地址
                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

initialize_request() 方法

 def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        返回一个初始化的request对象
        """
        parser_context = self.get_parser_context(request)
        # 这个 return 的Request 类不是Django自己的request了, 是drf框架里面自己定义的Request
        # 把原生django的request对象封装到该Request对象中,并且叫 _reuqest
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

initial()方法

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)  # 认证
        self.check_permissions(request)  # 权限
        self.check_throttles(request)  # 频率

Reuqest类

class Request(object):

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        ...
        #将原始的WSGIReqeust对象复制给_request
        self._request = request

        ....

    #调用旧的request.GET
    @property
    def query_params(self):return self._request.GET

    #调用旧的request.POST
    @property
    def POST(self):return QueryDict('', encoding=self._request._encoding)  
注:之后使用的request,都是restfromwork封装的Request对象

执行流程

"""
1.views.Book.as_view()
2.Book类中没有 as_view方法,去其父类APIView中找
3.在APIView中的as_view 方法中 执行了这样一句代码
    view = super(APIView, cls).as_view(**initkwargs)
    因此最终的as_view 还是从View 中的as_view来
4. 执行View里面的as_view()方法,返回view函数
5. url和视图函数之间建立绑定关系完毕,等待用户请求
6. 接收到用户请求,执行 Book--as_view(此处的as_view出处已再说上面,再次不再赘述)--View.as_view
7.执行self.dispatch()的时候,执行结果是什么就返回什么
8.开始找dispatch方法,self里面没有,Book类中也没有,在 APIView 中
9.开始执行APIView 中的 dispatch方法
10.最后找到http方法(get,post,put,delete)
11. 开始执行找到的方法(GET),self.get(),此时的self代表Book的实例化对象
12. 假设 请求为POST 请求,
    request.data  --->获取POST请求前端发送过来的数据
    request.FILES ----> 获取POST请求前端发送过来的文件
13. 开始执行reqeust.data
      @property
        def data(self):
            if not _hasattr(self, '_full_data'):
                self._load_data_and_files()
            return self._full_data
14. 执行该函数里面的  self._load_data_files()
15. 执行 self._load  self.files=self.parse()
16. 执行 parser = self.negotiator.select_parser(self, self.parsers)
17. 查找:self.parsers
18. 执行 并向上返回结果
"""

原文地址:https://www.cnblogs.com/qianzhengkai/p/11116691.html

时间: 2024-08-25 13:07:53

Django 之restfromwork 源码---APIView 分析的相关文章

Django 之restfromwork 源码分析之--视图组件

restframework 源码分析以及使用 mixins 中的五种类方法 from rest_framework import mixins # mixins 中一种有五种类 # 第一种:用户保存数据 class CreateModelMixin(object): """ Create a model instance. """ def create(self, request, *args, **kwargs): # 序列化的类的对象 ser

Django搭建及源码分析(三)---+uWSGI+nginx

每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当你将一个框架的优势使用到极致时一定是非常舒服和顺手的一件事.但同时也有可能衍生另一个问题,这个框架只解决了你的问题一,没有解决问题二.三等等,因此,就出现了多个框架/应用相结合的情况.比如Django + uWSGI + nginx. 本人初学python,找了一些实例进行了一些操作,以下纯属目前的

Django如何启动源码分析

Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字符串 解析manage.py #!/usr/bin/env python import os import sys if __name__ == "__main__": #os.environ.setdefault 方法可以修改系统环境变量,但是只能os.environ 只能影响到当前运行

Django Rest Framework源码剖析(三)-----频率控制

一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过代理限制访问频率等,但是django rest framework自身就提供了访问频率的控制,可以从代码本身做控制. 二.频率控制内部原理概述 django rest framework 中频率控制基本原理基于访问次数和时间,通过计算实现,当然我们也可以自己定义频率控制方法.基本原理如下: 启用频率

Django Rest Framework源码剖析(八)-----视图与路由

一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使用者而言不同的视图具有不同的功能,这样我们可以根据需求定制自己视图.以下是官网传送门:http://www.django-rest-framework.org/api-guide/views/ 在之前的文章中,由于参杂了权限.认证等(如果不了解请看博客的以前的文章),但在本章中基本可以不使用,所进使

又是正版!Win下ffmpeg源码调试分析二(Step into ffmpeg from Opencv for bugs in debug mode with MSVC)

最近工作忙一直没时间写,但是看看网络上这方面的资源确实少,很多都是linux的(我更爱unix,哈哈),而且很多是直接引入上一篇文章的编译结果来做的.对于使用opencv但是又老是被ffmpeg库坑害的朋友们,可能又爱又恨,毕竟用它处理和分析视频是第一选择,不仅是因为俩者配合使用方便,而且ffmpeg几乎囊括了我所知道的所有解编码器,但是正是因为这个导致了一些bug很难定位,所以有必要考虑一下如何快速定位你的ffmpeg bug. sorry,废话多了.首先给个思路: 1.使opencv 的hi

uboot移植——uboot源码目录分析

uboot移植(一)--uboot源码目录分析 本文分析的uboot是九鼎官方提供的,是对应s5pv210开发板x210bv3的uboot 一:uboot的概念及移植的原理. uboot就是在内核运行前的一段小程序,用来初始化硬件设备,建立内存空间映射图.从而将系统的软硬件带到合适的状态,主要功能就是为了启动内核,它将内核从flash中拷贝到ddr中,然后跳转到内核入口中,交由内核控制权,uboot严重依赖硬件,因此一个通用的uboot不太可能. 移植原理:uboot中有很多平行代码,各自属于各

netty 源码简单分析一

周末简单看了下netty5的源码,只看懂了个大概,记录下成果,方便下次再看的时候回忆. 上服务端代码: public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.grou

MapReduce job在JobTracker初始化源码级分析

mapreduce job提交流程源码级分析(三)中已经说明用户最终调用JobTracker.submitJob方法来向JobTracker提交作业.而这个方法的核心提交方法是JobTracker.addJob(JobID jobId, JobInProgress job)方法,这个addJob方法会把Job提交到调度器(默认是JobQueueTaskScheduler)的监听器JobQueueJobInProgressListener和EagerTaskInitializationListen