一、django rest_framework源码之总体流程剖析
1 序言
有如下django代码,视图层:
from django.http import HttpResponse
from rest_framework.views import APIView
class OrdersView(APIView):
def get(self , request , *args , **kwargs):
return HttpResponse('GET请求')
def post(self , request , *args , **kwargs):
return HttpResponse('POST请求')
def put(self , request , *args , **kwargs):
return HttpResponse('PUT请求')
def delete(self , request , *args , **kwargs):
return HttpResponse('DELETE请求')
路由配置为:
urlpatterns = [
……
url(r'^orders/', OrdersView.as_view()),
]
那么,可以在浏览器中通过链接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的源码,代码如下:
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
#继续调用父类中的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中的代码除去提示信息和注释就没有多少了,为什么呢?因为它将大部分的工作都交给了父类的as_view去做。代码中有一行“view = super(APIView, cls).as_view(**initkwargs)”,也就是通过super关键字继续调用父类as_view。APIView的父类是django自带的View类 在django.views.generic模块中,我们找到其中的as_view方法,源码如下所示:
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):
#用cls关键字实例化之类,也就是OrderViews
#这行代码相当于self = OrdersView(**initkwargs)
#所以之后的self就是OrdersView实例
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
#这才是最关键的地方:调用dispatch方法
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
return view
这个as_view代码也还是不多,主要就是在as_view方法内部定义了一个view方法,然后返回这个方法。但最关键的是,在这个as_view中调用了一个dispatch方法,大部分的工作都是在这个dispatch中完成的。
3 dispatch方法
dispatch在哪呢?Python会从调用处开始找(也就是OrdersView中开始找,可不是就近原则在View中开始找dispatch,虽然该类中确实有一个dispatch方法)。OrdersView中没有dispatch方法,接下来继续在OrdersView的父类(APIViews类)中寻找,果然就找到了,源码如下:
def dispatch(self, request, *args, **kwargs):
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:
#第二步:进行一下操作
# 1.处理版权信息
# 2.认证
# 3.权限
# 4.请求用户进行访问频率的限制
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
#第三步:通过反射获取请求方法(get、post、put…),并调用
#request.method的值(GET、POST…)是大写的,所以要用变成小写
if request.method.lower() in self.http_method_names:
#这个时候handler的值就是get、post、put等
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
#调用请求方法(get、post…)
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
dispatch方法是最核心的一个方法,在这个方法执行主要包括三个过程:
第一步:对从浏览器端传过来的原生request进行包装加工。
第二步:进行处理版权信息、认证、权限、请求用户进行访问频率的限制。
第三步:通过反射获取请求方法(get、post、put…),并调用。
下面继续结合源代码对上述三个步骤进行剖析。
第一个步骤是通过调用initialize_request方法来完成的,initialize_request方法也是在APIView中,源码如下:
def initialize_request(self, request, *args, **kwargs):
# 把请求转化成一个字典
parser_context = self.get_parser_context(request)
return Request(
request,#原生的request
parsers=self.get_parsers(),#解析数据
authenticators=self.get_authenticators(),#认证
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
所以,在第一步中,initialize_request方法在原生的request的基础上添加了一些新的参数进去,包括数据解析实例、认证实例等,那么是怎么添加的呢?以authenticators(认证)为例进行说明。找到get_authenticators方法,源码如下:
def get_authenticators(self):
#authentication_classes是类名列表,所以auth就是类名,auth()就是类实例
return [auth() for auth in self.authentication_classes]
在get_authenticators方法中,以列表解析式的方式生成了一个包含所有认证类实例的列表。
第二步操作中进行处理版权信息、认证、权限、请求用户进行访问频率的限制,在initial方法中进行。Initial源码如下:
def initial(self, request, *args, **kwargs):
"""
在调用方法处理程序之前运行需要发生的任何事情(例如:认证、权限、访问频率控制)。
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
self.perform_authentication(request)#执行认证
self.check_permissions(request)#检查权限
self.check_throttles(request)#频率控制
#上述3种操作,如果都通过,那么返回None,如果不通过则抛出异常
在initial方法中并没有返回值,因为如果认证、权限、频率控制都通过了,那程序继续执行第三步。如果不通过,那么抛出异常,第三步也不会执行了。
第三步就是执行get、post等请求方法了。这一步的操作关键代码都是在dispatch本类中进行,判断是哪种请求方法并不是通过类似于request.method==”get”,而是通过反射的方式,在上述dispatch源码中我做了注释,看源码就好了。
执行请求方法(get、post…)时,还是优先调用OrdersView类中的方法,没有在去父类中找。本例中因为OrdersView中定义了get方法,所以会执行自定制的get方法,返回HttpResponse实例。
至此,所有关键操作已经完成,剩下都工作就是就得到请求方法中生成的HttpResponse实例依次向上返回。
一、django rest_framework源码之总体流程剖析的更多相关文章
- 二、django rest_framework源码之认证流程剖析
1 绪言 上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法.在dispatch方法中,在调用了一个initial方法,所有的认证.权限 ...
- 三、django rest_framework源码之权限流程剖析
1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...
- 六、django rest_framework源码之解析器剖析
1 绪论 网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framewor ...
- 七、django rest_framework源码之视图
1 绪言 当大家看大这篇博文的时候,应该对Django rest_framework中的CBV有所了解了,大致来说就是通过定义类来继承APIView类,并在类中定义get.post.put.delet ...
- 五、django rest_framework源码之版本控制剖析
1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...
- 四、django rest_framework源码之频率控制剖析
1 绪言 权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码. 2 源码分析 访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始. ...
- Django session 源码流程
流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...
- Django学习——Django settings 源码、模板语法之传值、模板语法之获取值、模板语法之过滤器、模板语法之标签、自定义过滤器、标签、inclusion_tag、模板的导入、模板的继承
Django settings 源码 """ 1.django其实有两个配置文件 一个是暴露给用户可以自定义的配置文件 项目根目录下的settings.py 一个是项目默 ...
- django --- DetailView源码分析
[背景] 最近在看django官方文档的class-based-views这一节的时候一直不得要领,感觉自己清楚,但是回想起来又没有脉络:于是没有办法只 能是“暗中观察”django的源码了. 刚打开 ...
随机推荐
- oracle04--伪列
1. 伪列 1.1. 什么是伪列 伪列是在ORACLE中的一个虚拟的列. 伪列的数据是由ORACLE进行维护和管理的,用户不能对这个列修改,只能查看. 所有的伪列要得到值必须要显式的指定. 最常用的两 ...
- CSS line-height应用
一.固定高度的容器,单行文本垂直居中 代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf- ...
- vue中使用cookie记住用户上次选择(本次例子中为下拉框)
最近工作中碰到一个需求,添加一条数据时,自动记住上次选择的下拉框的数据,刚开始觉得没思路,后来请教了项目组长,组长直接一句,这不很简单吧,直接用cookie,我:....... 好吧,都王的差不多了, ...
- Testbench学习——$fopen/$display/$fclose
昨天在用Vivado写Testbench顶层时,为了以后便于数据的存储导出分析,需要用的文件数据记录的功能,于是,下面谈谈$fopen/$display/$fclose这三者的用法. $fopen—— ...
- 浅谈mysql配置优化和sql语句优化【转】
做优化,我在这里引用淘宝系统分析师蒋江伟的一句话:只有勇于承担,才能让人有勇气,有承担自己的错误的勇气.有承担错误的勇气,就有去做事得勇气.无论做什么事,只要是对的,就要去做,勇敢去做.出了错误,承担 ...
- github 优秀的开源项目
https://github.com/wlcaption/AndroidMarket---- 这是手机应用商店,包含应用的下载,用户中心等内容 https://github.com/wlcaption ...
- if(a==1) & if(1==a) 区别
[前提] 在公司参加项目时,看到前辈写if比较数值是否相等,经常会写 if(1==a) ,觉得有些奇怪,如是乎,将调查结果写下来记录一下. [结果] if(a==1) 与 if(1==a)是没有什么区 ...
- python基础--re模块
常用正则表达式符号 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r& ...
- [转] Cacti+Nagios监控平台完美整合
Cacti+Nagios监控平台完美整合 http://os.51cto.com/art/201411/458006.htm 整合nagios+cacti+微信.飞信实现网络监控报警 http://b ...
- shell中的特殊符号总结
在shell中常用的特殊符号罗列如下: # ; ;; . , / \\ 'string'| ! $ ${} $? $$ $* \"string\"* ** ...