django rest_fremework源码流程总体剖析

Posted chenhuabin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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实例依次向上返回。

 

以上是关于django rest_fremework源码流程总体剖析的主要内容,如果未能解决你的问题,请参考以下文章

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

django-admin的源码流程

Django2.0.6-Xadmin后台源码安装流程(python 3.8+django 2.0)

Django Admin源码流程

Django--admin源码流程

Django_admin源码流程