在django—CBV源码分析中,我们是分析的from django.views import View
下的执行流程,以下是代码
from django.views import View
class IndexView(View):
def get(self,request, *args, **kwargs):
return HttpResponse("ok")
def dispatch(self, request, *args, **kwargs):
ret = super(IndexView,self).dispatch(request, *args, **kwargs)
return HttpResponse(ret)
这篇博客我们就来了解下APIView
是如何执行的,跟django.views
模块下的view
有何关联?
我们依然从url配置入手分析
url(r"books/$",views.BookView.as_view())
as_view
方法代码如下
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.
This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
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
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
类是继承View
类,view
类正式from django.views import View下的View
,
先看as_view
方法中的view = super(APIView, cls).as_view(**initkwargs)
的这行代码,
是调用了父类View
中的as_view
方法,这里的initkwargs
,及其父类的View
中的as_view
方法执行流程,之类就不在赘述了,简单说就是在如下IndexView
类的执行流程就是
先去执行print("dispatch")
–>然后在去执行print("get")
方法–>然后在去执行super(IndexView,self).dispatch(request, *args, **kwargs)
–>最后执行return HttpResponse(ret)
具体流程去看我这票博客开头的博客链接
from django.views import View
class IndexView(View):
def get(self,request, *args, **kwargs):
print("get")
return HttpResponse("ok")
def dispatch(self, request, *args, **kwargs):
print("dispatch")
ret = super(IndexView,self).dispatch(request, *args, **kwargs)
print("ret",ret )
return HttpResponse(ret)
所以在APIView
类中,我们重点看下return csrf_exempt(view)
做了什么操作?
def csrf_exempt(view_func):
def wrapped_view(*args, **kwargs):
return view_func(*args, **kwargs)
wrapped_view.csrf_exempt = True
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
wrapped_view.csrf_exempt = True
意思是取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件,然后将csrf_exempt
函数中的内置函数wrapped_view
赋值wrapped_view.csrf_exempt = True
,使其拥有该属性,
接下来看 wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
函数之前,
我们先看下assigned=available_attrs(view_func)
def available_attrs(fn):
if six.PY3:
return WRAPPER_ASSIGNMENTS
else:
return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
大概意思就是针对py3
或者其他版本做了一些判断处理,最后通过WRAPPER_ASSIGNMENTS
定为到
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
这个逻辑跟我们上一篇的CBV源码有共同之处,就是为某个方法或者函数,添加某些属性
接下来我们看wraps
函数吧
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
点击partial
跟进
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
wrapped是我们在def csrf_exempt(view_func):
函数中的参数,也就是as_view
的返回值,
最后通过functools
模块下的partial
类进行装饰构造,partial 这个东西是针对函数起作用的,并且是部分的,
场景:有这样的函数:get_useragent(request) 用来获取用户浏览器的ua信息,但是这个函数又不是在主体函数(执行页面渲染的函数)get时调用的,只在模板中的一个filter中调用的(可以理解是在模板渲染时调用的),而filter在执行的时候是不能添加参数的,哪你要怎么处理。
这时partial就得闪亮登场,如下是代码,
def __new__(*args, **keywords):
if not args:
raise TypeError("descriptor '__new__' of partial needs an argument")
if len(args) < 2:
raise TypeError("type 'partial' takes at least one argument")
cls, func, *args = args
if not callable(func):
raise TypeError("the first argument must be callable")
args = tuple(args)
if hasattr(func, "func"):
args = func.args + args
tmpkw = func.keywords.copy()
tmpkw.update(keywords)
keywords = tmpkw
del tmpkw
func = func.func
self = super(partial, cls).__new__(cls)
self.func = func
self.args = args
self.keywords = keywords
return self
def __call__(*args, **keywords):
if not args:
raise TypeError("descriptor '__call__' of partial needs an argument")
self, *args = args
newkeywords = self.keywords.copy()
newkeywords.update(keywords)
return self.func(*self.args, *args, **newkeywords)
最后在csrf_exempt
函数中的wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)这里写代码片
传入参数wrapped_view
,通过对象可调用功能,进行调用__call__
方法
到此as_view
分析完毕,以上代码好多有迷惑的点,我分析的时候也是很多猜想的
上篇CBV
源码分析中我们知道,当as_view
执行后,当浏览器访问某个api接口时候,
就会先触发dispatch
,然后在dispatch
中,如下是父类的dispatch
方法
def dispatch(self, request, *args, **kwargs):
# 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.
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)
然而API
方法中也有自己的dispatch
方法,会执行子类的方法,而不是去执行父类的dispatch
方法,以下是代码
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
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
以上dispatch
方法中,通过self.initialize_request(request, *args, **kwargs)
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
)
通过initialize_request
进一步分装成自己的Request
类
然后self.initial(request, *args, **kwargs)
,
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
完善request
请求的一些注意事项,例如用户登录、检测权限等等
然后response = handler(request, *args, **kwargs)
这里面是执行了对应的请求操作,如get\post
请求,也就是执行了我们自定义视图里面的get方法等,在处理完毕后,赋值response
然后作为参数进行如下的操作
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
# Make the error obvious if a proper response is not returned
assert isinstance(response, HttpResponseBase), (
'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
'to be returned from the view, but received a `%s`'
% type(response)
)
if isinstance(response, Response):
if not getattr(request, 'accepted_renderer', None):
neg = self.perform_content_negotiation(request, force=True)
request.accepted_renderer, request.accepted_media_type = neg
response.accepted_renderer = request.accepted_renderer
response.accepted_media_type = request.accepted_media_type
response.renderer_context = self.get_renderer_context()
# Add new vary headers to the response instead of overwriting.
vary_headers = self.headers.pop('Vary', None)
if vary_headers is not None:
patch_vary_headers(response, cc_delim_re.split(vary_headers))
for key, value in self.headers.items():
response[key] = value
return response
随后self.response = self.finalize_response(request, response, *args, **kwargs)
返回,这一部操作,跟它的父类View
是不同的,
在继承APIView的视图中,get\post需要返回HttpResponse
,然后在通过self.finalize_response
进一步封装,最后返回
最后在dispatch
中
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
那么接下来看下我们本片博客,也就是继承APIView
的编写的视图
class BookView(APIView):
def get(self,request,*args,**kwargs):
book_list = Book.objects.all()
# 当我们输入参数many = True时, serializer还能序列化queryset
bs = BookSerializers(book_list, many=True)
# print(bs.data) # 序列化的结果
return Response(bs.data)
我们需要返回Json
格式的数据,下篇博客我们就看下是如何进行序列化的