drf CBV和APIView源码解读
CBV源码
1. 入口点:
视图
class Books(View):
def get(self, request):
return JsonResponse(back_dic)
from django.views import View
url(r'^books/',views.Book.as_view()) # 类调用的方法绑定方法
@classonlymethod
def as_view(cls, **initkwargs):
这里我们可以看到as_view上面加了一个类方法,去@classonlymethod它继承的classmethod的方法,这个重定义的类方法比classmethod更强大,Book.as_view(),调用的时候可以不用加括号
2. 请求来时
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs): # 档次请求的request,如request.method,request.POST
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
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
update_wrapper(view, cls.dispatch, assigned=())
return view
请求来了,路径匹配,执行函数的内存地址(request)
,会把当前请求的request
对象当作第一个参数传过来,执行as_view
的内层函数 def view()
,cls(**initkwargs)
实例化得到一个对象,cls
就是Books
对象,用反射判断有没有get
这个方法,self.request = request
把当前请求的request
赋值到Books
对象里面的request
,在视图函数里print(self.request)这个request
和参数里面的request
是一样的,指向一个内存地址。
return self.dispatch(request, *args, **kwargs)
,self.dispatch
,self是books,所以要到当前类视图函数寻找dispatch方法,没有才去到View去找发现了dispatch方法.
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
request就是当次请求的request.lower()转成小写,判断视图函数里面有没有self.http_method_names
这个方法,在强调一遍这里的self始终是Book对象,没有就去父类View里面找到这个方法http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
,反射的意思: handler=getattr(self,'get')
,你写的Book
类的get
方法的内存地址,执行get(request)
,原理还是FBV.
提示:如何我们只接受get请求可以在视图函数里面重写http_method_names
APIView源码
它的原理就是通过中间件之后执行的代码
1. 入口:
视图
class BooksAPIView(APIView)
def get(self,request):
pass
url
url(r'^books/',views.BooksAPIView.as_view()),
这里的和CBV的原理一样,但是这里的as_view,前面加了@classmethod,这里调的是APIView里面的as_view方法
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view)
我们看到super调用了父类(View)的as_view方法,最后还是CBV里面的内层函数,这里的view是父类类面return 返回的view,csrf_exempt
:不管你有没有把中间件里面的csrf中间件去掉, 都没有csrf认证了,
2.dispatch
我们把目光继续跳到父类view里面,执行到dispatch里面后,先去BooksAPIView去找发现没有这个函数,就绪它继承的父类APIView里面去找,我们可以找到这个dispatch方法,没有去APIView父类(View),如果想看查找顺序用mro,
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 重新包装一个request对象,以后再用的时候就是新的request对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
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
这个是APIView的dispatch方法,请求来了在路由匹配上执行到了view里面的内层函数,调用了dispatch,按照查找顺序就到了这里面, request = self.initialize_request(request, *args, **kwargs)
这个request是当次请求的request赋值给了self.initialize_request
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
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
)
这个代码先忽略掉,self.initialize_request(request, *args, **kwargs)
的request是当次请求的request, request = self.initialize_request,这个request是一个新的request对象,self.request = request把这个新的request赋值给了当前类的 request,视图函数里面的request已经不是django原生的request,是drf中间定义的request对象,我们导入from rest_framework.request import Request
查看到,
class BooksAPIView(APIView)
def get(self,request):
print(request.data)
验证:原生的request里面没有data这个属性,这个是drf的request的data属性。
3.drf的Request类
3.1 原生request的封装
上面我们执行到request = self.initialize_request(request, *args, **kwargs)
的self调用
initialize_request这个方法,到视图类里面去找没有,再到APIView里面找到了initialize_request这个方法
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
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
)
这里return 一个Request对象,里面有一个原生的request对象,原生的request对象被封装到了Request
class Request:
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance.
- parsers_classes(list/tuple). The parsers to use for parsing the
request content.
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)
self._request = request在这里是原生的request对象,被包装到class Request可以用from rest_framwork.request import Request
3.2 原生method ,request.data,request.query_params
视图函数
print(request.method)
return HttpResponse('ok')
当我们打印的时候为什么还能打印出原来request对象的属性呢,这个是点拦截属性, 魔法语法,
点的时候会调用__getattr__
这个是Request重新写了这个方法
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
通过反射如果取method这个方法,到self._request去取出来的,如果有异常通过下面的方式取,以后是由request对象就像使用之前的request对象一样的,其实就是通过魔法方法,让你发现跟原来的一样,没有发觉。
print(request.data),这个是方法,@property修饰了
源码:request.data
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
我们我们一直按照_full_data点下去,其实就是一个字典,不管使用了什么方式,什么编码传过来的数据都在request.data
def post(self,request):
print(request.data)
return HttpResponse('OK')
urlencoded:<QueryDict: {'name': ['aaaa'], 'age': ['111']}>
json:{'name': 'zzz', 'age': 333}
我们发现返回的是一个QueryDict我们导入模块看一下from django.http import QueryDict
class QueryDict(MultiValueDict):
def copy(self):
"""Returns a mutable copy of this object."""
return self.__deepcopy__({})
它继承了字典,但是不能改,但是它copy一遍在修改
get请求传过来的数据
request.GET这个能取出来,在Request又写了一个
print(request.query_params)在get请求,地址参数里
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
这样写是为了符合rest_framwork的规范,文件也是一样重写了
好我们收回,把思路跳到我们查询请求的方式APIView里面的 dispatch
try:
# 三大认证模块
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
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
这里的异常处理无论出现什么错误,返回的都是json格式的错误。