django rest_fremework源码流程总体剖析

1. 序言

有如下django代码,视图层:

 1 from django.http import HttpResponse
 2 from rest_framework.views import APIView
 3 class OrdersView(APIView):
 4     def get(self , request , *args , **kwargs):
 5         return HttpResponse(‘GET请求‘)
 6     def post(self , request , *args , **kwargs):
 7         return HttpResponse(‘POST请求‘)
 8     def put(self , request , *args , **kwargs):
 9         return HttpResponse(‘PUT请求‘)
10     def delete(self , request , *args , **kwargs):
11         return HttpResponse(‘DELETE请求‘)

路由配置为:

1 urlpatterns = [
2     ……
3     url(r‘^orders/‘, OrdersView.as_view()),
4 ]

那么,可以在浏览器中通过链接http://127.0.0.1:8000/orders/进行访问,可以看到,浏览器页面中输出了“GET请求”的字样。说明OrdersView类中的get方法被调用了。那么,从浏览器中通过“http://127.0.0.1:8000/orders/”发出请求到服务器返回response经历了怎么样的一个过程呢?get方法又是如何被调用的呢?

2. As_view方法

当链接“http://127.0.0.1/8000/orders/”的request请求发到服务器后,通过urls.py中的路由匹配到“url(r‘^orders/‘, OrdersView.as_view())”这一项。有一点必须明确的是,一个url配置项就对应一个方法,FBV(基于函数的试图)如此,CBV(基于类的试图)。所以,“url(r‘^orders/‘, OrdersView.as_view())”对应的最直观上来说就是OrdersView类中的as_view方法,也就是说,当这个url被匹配到后,OrdersView的as_view立即被调用。

但我们在OrdersView中并没有定义as_view方法呀,那as_view会在哪呢?答:在父类中。从定义OrdersView类代码可以看出,OrdersView继承类rest_framework.views中的 APIView类,打开APIView就可以找到as_view的源码,代码如下:

 1 def as_view(cls, **initkwargs):
 2     if isinstance(getattr(cls, ‘queryset‘, None), models.query.QuerySet):
 3         def force_evaluation():
 4             raise RuntimeError(
 5                 ‘Do not evaluate the `.queryset` attribute directly, ‘
 6                 ‘as the result will be cached and reused between requests. ‘
 7                 ‘Use `.all()` or call `.get_queryset()` instead.‘
 8             )
 9
10         cls.queryset._fetch_all = force_evaluation
11     #继续调用父类中的as_view方法
12     view = super(APIView, cls).as_view(**initkwargs)
13     view.cls = cls
14     view.initkwargs = initkwargs
15     # Note: session based authentication is explicitly CSRF validated,
16     # all other authentication is CSRF exempt.
17     return csrf_exempt(view)

可以看出,APIView中的代码除去提示信息和注释就没有多少了,为什么呢?因为它将大部分的工作都交给了父类的as_view去做。代码中有一行“view = super(APIView, cls).as_view(**initkwargs)”,也就是通过super关键字继续调用父类as_view。APIView的父类是django自带的View类 在django.views.generic模块中,我们找到其中的as_view方法,源码如下所示:

 1 def as_view(cls, **initkwargs):
 2     """Main entry point for a request-response process."""
 3     for key in initkwargs:
 4         if key in cls.http_method_names:
 5             raise TypeError("You tried to pass in the %s method name as a "
 6                             "keyword argument to %s(). Don‘t do that."
 7                             % (key, cls.__name__))
 8         if not hasattr(cls, key):
 9             raise TypeError("%s() received an invalid keyword %r. as_view "
10                             "only accepts arguments that are already "
11                             "attributes of the class." % (cls.__name__, key))
12     def view(request, *args, **kwargs):
13         #用cls关键字实例化之类,也就是OrderViews
14         #这行代码相当于self = OrdersView(**initkwargs)
15         #所以之后的self就是OrdersView实例
16         self = cls(**initkwargs)
17         if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
18             self.head = self.get
19         self.request = request
20         self.args = args
21         self.kwargs = kwargs
22         #这才是最关键的地方:调用dispatch方法
23         return self.dispatch(request, *args, **kwargs)
24     view.view_class = cls
25     view.view_initkwargs = initkwargs
26     update_wrapper(view, cls, updated=())
27     update_wrapper(view, cls.dispatch, assigned=())
28     return view

这个as_view代码也还是不多,主要就是在as_view方法内部定义了一个view方法,然后返回这个方法。但最关键的是,在这个as_view中调用了一个dispatch方法,大部分的工作都是在这个dispatch中完成的。

3. dispatch方法

dispatch在哪呢?Python会从调用处开始找(也就是OrdersView中开始找,可不是就近原则在View中开始找dispatch,虽然该类中确实有一个dispatch方法)。OrdersView中没有dispatch方法,接下来继续在OrdersView的父类(APIViews类)中寻找,果然就找到了,源码如下:

 1 def dispatch(self, request, *args, **kwargs):
 2     self.args = args
 3     self.kwargs = kwargs
 4     #第一步:对从浏览器端传过来的原生request进行包装加工
 5     request = self.initialize_request(request, *args, **kwargs)
 6     self.request = request
 7     self.headers = self.default_response_headers  # deprecate?
 8     try:
 9     #第二步:进行一下操作
10             # 1.处理版权信息
11             # 2.认证
12             # 3.权限
13             # 4.请求用户进行访问频率的限制
14         self.initial(request, *args, **kwargs)
15         # Get the appropriate handler method
16     #第三步:通过反射获取请求方法(get、post、put…),并调用
17         #request.method的值(GET、POST…)是大写的,所以要用变成小写
18         if request.method.lower() in self.http_method_names:
19             #这个时候handler的值就是get、post、put等
20             handler = getattr(self, request.method.lower(),
21                               self.http_method_not_allowed)
22         else:
23             handler = self.http_method_not_allowed
24         #调用请求方法(get、post…)
25         response = handler(request, *args, **kwargs)
26     except Exception as exc:
27         response = self.handle_exception(exc)
28     self.response = self.finalize_response(request, response, *args, **kwargs)
29     return self.response

dispatch方法是最核心的一个方法,在这个方法执行主要包括三个过程:

第一步:对从浏览器端传过来的原生request进行包装加工。

第二步:进行处理版权信息、认证、权限、请求用户进行访问频率的限制。

第三步:通过反射获取请求方法(get、post、put…),并调用。

下面继续结合源代码对上述三个步骤进行剖析。

第一个步骤是通过调用initialize_request方法来完成的,initialize_request方法也是在APIView中,源码如下:

 1 def initialize_request(self, request, *args, **kwargs):
 2     # 把请求转化成一个字典
 3     parser_context = self.get_parser_context(request)
 4     return Request(
 5         request,#原生的request
 6         parsers=self.get_parsers(),#解析数据
 7         authenticators=self.get_authenticators(),#认证
 8         negotiator=self.get_content_negotiator(),
 9         parser_context=parser_context
10     )

所以,在第一步中,initialize_request方法在原生的request的基础上添加了一些新的参数进去,包括数据解析实例、认证实例等,那么是怎么添加的呢?以authenticators(认证)为例进行说明。找到get_authenticators方法,源码如下:

1 def get_authenticators(self):
2     #authentication_classes是类名列表,所以auth就是类名,auth()就是类实例
3     return [auth() for auth in self.authentication_classes]

在get_authenticators方法中,以列表解析式的方式生成了一个包含所有认证类实例的列表。

第二步操作中进行处理版权信息、认证、权限、请求用户进行访问频率的限制,在initial方法中进行。Initial源码如下:

 1 def initial(self, request, *args, **kwargs):
 2     """
 3     在调用方法处理程序之前运行需要发生的任何事情(例如:认证、权限、访问频率控制)。
 4     """
 5     self.format_kwarg = self.get_format_suffix(**kwargs)
 6     neg = self.perform_content_negotiation(request)
 7     request.accepted_renderer, request.accepted_media_type = neg
 8     version, scheme = self.determine_version(request, *args, **kwargs)
 9     request.version, request.versioning_scheme = version, scheme
10     self.perform_authentication(request)#执行认证
11     self.check_permissions(request)#检查权限
12     self.check_throttles(request)#频率控制
13     #上述3种操作,如果都通过,那么返回None,如果不通过则抛出异常

在initial方法中并没有返回值,因为如果认证、权限、频率控制都通过了,那程序继续执行第三步。如果不通过,那么抛出异常,第三步也不会执行了。

第三步就是执行get、post等请求方法了。这一步的操作关键代码都是在dispatch本类中进行,判断是哪种请求方法并不是通过类似于request.method==”get”,而是通过反射的方式,在上述dispatch源码中我做了注释,看源码就好了。

执行请求方法(get、post…)时,还是优先调用OrdersView类中的方法,没有在去父类中找。本例中因为OrdersView中定义了get方法,所以会执行自定制的get方法,返回HttpResponse实例。

至此,所有关键操作已经完成,剩下都工作就是就得到请求方法中生成的HttpResponse实例依次向上返回。

原文地址:https://www.cnblogs.com/chenhuabin/p/9978468.html

时间: 2024-07-31 04:01:51

django rest_fremework源码流程总体剖析的相关文章

django restfulwork 源码剖析

概要: 1.restful 规范(建议); 2. django rest framework框架 内容回顾: 1.开发模式; - 普通开发模式(前后端放在一起写) - 前后端分离 好处: 后端一套,前端使用app,pc等: 2. 后端开发 为前端提供URL(API的开发) 注:永远返回Httpresponse 3. django Fbv 和Cbv 一.Django CBV和FBV 在视图中,cbv支持四中method,如下: from django.views import View class

Django学习【第29篇】:django-admin的源码流程

django-admin的源码流程 一.admin的源码流程 首先可以确定的是:路由关系一定对应一个视图函数 a.当点击运行的时候,会先找到每一个app中的admin.py文件,并执行 b.执行urls.py admin.site是什么? admin.site,urls    返回的是一个元组,里面的第一个元素是一个列表 django-admin的源码流程 我们自己生成的动态的访问url ====================================初级版===============

Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax csrf相关装饰器 在CBV上加csrf装饰器 Django settings源码剖析及模仿使用 Django settings源码剖析 查看内部配置文件 模仿使用 Auth模块 auth简介 auth模块常用方法 创建用户 校验用户名和密码 保存用户登录状态 判断当前用户是否登录 校验原密码 修改密

ios局域网联机—苹果官方源码之WiTap剖析(一)(二)

http://www.it165.net/pro/html/201204/2094.html http://www.it165.net/pro/html/201204/2165.html 在过去的时间里,我一直在考虑的事情是,我该写一篇什么样的文章呢?之前的两篇文章都是先有问题,然后我才有目的的解决问题,现在我的困扰是,我不知道该写什么了呵呵.因为其实,大多数的问题,只要在网上搜索一下(google远比baidu要强得多),基本上都能找到解决的办法,已经有了许多相关方面的教程或参考资料了,我并不

Exchanger源码Android版剖析

Exchanger是一个针对线程可以结对交换元素的同步器.每条线程把某个对象作为参数调用exchange方法,与伙伴线程进行匹配,然后再函数返回的时接收伙伴的对象.另外,Exchanger内部实现采用的是无锁算法,能够大大提高多线程竞争下的吞吐量以及性能. 算法实现 基本方法是维持一个"槽"(slot),这个槽是保持交换对象的结点的引用,同时也是一个等待填满的"洞"(hole).如果一个即将到来的"占领"(occupying)线程发现槽为空,然后

Django 【第二十五篇】Django admin源码解析

一.admin的源码流程 首先可以确定的是:路由关系一定对应一个视图函数 a.当点击运行的时候,会先找到每一个app中的admin.py文件,并执行 b.执行urls.py admin.site是什么? admin.site,urls    返回的是一个元组,里面的第一个元素是一个列表 django-admin的源码流程 我们自己生成的动态的访问url ====================================初级版========================= from dj

rest_framework-02-权限-内置权限源码流程

权限 问题:不同视图不同权限可以访问 1.models.py from django.db import models class UserInfo(models.Model): user_type_choices = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP'), ) user_type = models.IntegerField(choices=user_type_choices) # username = models.CharField(max_length=3

django-admin的源码流程

django-admin的源码流程 一.admin的源码流程 首先可以确定的是:路由关系一定对应一个视图函数 a.当点击运行的时候,会先找到每一个app中的admin.py文件,并执行 b.执行urls.py admin.site是什么? admin.site,urls    返回的是一个元组,里面的第一个元素是一个列表 django-admin的源码流程 我们自己生成的动态的访问url ====================================初级版===============

MyBatis 源码篇-MyBatis-Spring 剖析

本章通过分析 mybatis-spring-x.x.x.jar Jar 包中的源码,了解 MyBatis 是如何与 Spring 进行集成的. Spring 配置文件 MyBatis 与 Spring 集成,在 Spring 配置文件中配置了数据源.SqlSessionFactory.自动扫描 MyBatis 中的 Mapper 接口.事务管理等,这部分内容都交由 Spring 管理.部分配置内容如下所示: <?xml version="1.0" encoding="U