本博客默认读者有过django学习基础,了解django基本运行方式。
先来看一个关于面向对象的知识点,在这里我们创建一个类Foo,通过__init__方法赋值:
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
obj = Foo(1, 2)
print(obj.a) #1
print(obj.b) #2
那么不出意外的,上面代码会输出a和b的值。
其实在获取对象成员时,默认会调用 __getattribute__
方法,如果我们没有自己定义这个方法,就回去父类object里面去找。
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __getattribute__(self, item):
print("调用了__getattribute__方法,获取到了:",item)
return super().__getattribute__(item)
obj = Foo(1, 2)
print(obj.a)
print(obj.b)
输出以下结果:
注意:如果获取对象中没有的成员,会报错,如
print(obj.c)
#AttributeError: 'Foo' object has no attribute 'c'
如果想要获取对象中不存在的成员,则可以通过定义 __getattr__
实现,例如:
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __getattribute__(self, item):
print("调用了__getattribute__方法,获取到了:",item)
return super().__getattribute__(item)
def __getattr__(self, item):
print("调用了__getattribute__方法,获取到了:",item)
return 123
obj = Foo(1, 2)
print(obj.a)
print(obj.b)
print(obj.c)
会输出以下结果
根据输出打印的顺序,我们可以看出,当访问对象中的成员时:
先执行自己的 __getattribute__
再执行父类的__getattribute__
是自己成员,直接获取并返回
不是自己成员,调用__getattr__
,返回的值赋值给该成员
也就是说假如你通过__getattr__
访问一个不存在的成员,return的值将会赋值给该成员。
定义一个类Base,并实例化一个对象base,再定义一个类Foo,实例化一个对象obj,在obj中封装base:
class Base(object):
def v1(self):
print("这是Base的v1")
def v2(self):
print("这是Base的v2")
class Foo(object):
def __init__(self,a,b):
self.a=a
self.b=b
base=Base()
obj=Foo(base,10)
obj.a.v1()#这是Base的v1
obj.a.v2()#这是Base的v2
在实例化Foo这个类时,我们调用了init方法,将对象base作为参数传给了obj的成员a,这样base就封装在了obj中,obj就可以调用base中的方法,获取它的成员。
我们先来写一个简单的视图关系。
urls.py中定义路由:
#urls.py
from django.urls import path
from app01.views import view
urlpatterns = [
path('user/', view.UserView.as_view()),
]
在这里我们可以’ctrl’+鼠标左键 产看as_view的源码,在这里我复制了一部分比较重要的。
#views.py(drf)
class APIView(View):
def as_view(cls, **initkwargs):
#父类的as_view方法
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view)
as_view是APIView类的一个成员方法,而APIView类是继承View这个类的,值得一提的是,View这个类是django的源代码中定义在base.py的一个类。在as_view的第6行代码中,我们发现调用了父类的as_view方法并把返回值赋值给了view,下面我们来看看django中as_view的源代码,同样的我只截取了一部分方便理解。
#base.py(django)
class View:
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
return self.dispatch(request, *args, **kwargs)
return view
在这里我们重点关注函数的参数和返回值,在view方法中,调用了dispatch方法对request和其他参数进行操作,并将结果返回,那么我们继续看看dispatch,注意,调用成员函数方法时,会先找自身有没有这个函数,如果有就用自己的,没有才会去父类找,而在drf的views.py中就有对dispatch的定义。
#views.py(drf)
class APIView(View):
def dispatch(self, request, *args, **kwargs):
#新的requst
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.initial(request, *args, **kwargs)
#反射执行getattr方法,并传到新的request里面
handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
response = handler(request, *args, **kwargs)
return self.response
源码中对dispatch的解释是“这里的dispatch和django的dispatch很像”,而django中的dispatch的作用是
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
翻译过来大概就是“尝试去反射给正确的方法(函数),如果这个方法不存在就报错”,也就是说这个dispatch的作用是将request反射给正确的函数方法的。是不是有点像快递小哥,每个快递小哥都有自己负责的一片区域,收到一个包裹就送到指定的地方,要是收到不是自己负责区域的包裹就报错不干了。
注意在上面的代码中的第四行,调用了initialize_request方法对request进行了操作,你看着initalize这个单词是不是感觉要对request进行一种类似初始化的操作啊,那么前面讲了这么一大堆终于讲到今天的主题了。
下面我们来看看initialize_request这个函数
##views.py(drf)
class APIView(View):
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
)
到这里我们终于发现了,drf对request进行了这样的封装操作,把原有的requst请求和其他的参数一起封装成了一个新的Request对象,并且返回给了原来的request,具体封装了些什么内容呢,来看看Request这个类是怎么构成的。
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
到这里还没完,我们继续来看看这个dispatch。
handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
这里调用了我们前面铺垫的getattr函数, 反射执行get方法,获得各种参数,并传到新的request里面。
后面我们会来分析一下认证,权限,限流三种组件,通过源码+案例的方式进行讲解。