DRF請求生命周期解析
- CBV的路由糙申,調(diào)用視圖的
as_view()
,一般業(yè)務(wù)視圖很少定義自己的as_view()
碧注,所有會(huì)找到父類的as_view()
,所以請求走的是APIView
的as_view()
函數(shù) - 在
APIView
的as_view
調(diào)父類的(django原生)的as_view
:view = super().as_view(**initkwargs)
,還禁用了csrf
認(rèn)證,return csrf_exempt(view)
- 在父類的
as_view
中dispatch
方法請求走的又是APIView
的dispatch
- 完成任務(wù)方法交給視圖類的請求函數(shù)處理,得到請求的響應(yīng)結(jié)果烫幕,返回給前臺(tái)
請求步驟拆解
1. 請求進(jìn)來走的是APIView
的as_view()
函數(shù)
APIView
的as_view()
函數(shù)主要做了兩件事
- 調(diào)用父類的
as_view
,就是View
的as_view()
方法 - 禁用
scrf
的token
認(rèn)證:csrf_exempt(view)
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
schema = DefaultSchema()
@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().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)
2. View中的as_view(),調(diào)用self.dispatch()
完成請求的分發(fā)
class View:
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
for key, value in kwargs.items():
setattr(self, key, value)
@classonlymethod
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):
# 實(shí)例化產(chǎn)Mylogin的對象妆棒, self = Mylogin(**initkwargs)
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
#dispatch返回什么澡腾,瀏覽器就會(huì)收到什么
return self.dispatch(request, *args, **kwargs)
# 對象在查找屬性或者方法的時(shí)候,一定要默念糕珊,
# 先從對象這里找动分,然后從產(chǎn)生對象的類里找,最后從父類里找
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
# 這個(gè)方法僅僅是在給對象新增屬性
self.request = request
self.args = args
self.kwargs = kwargs
3.APIView
類中定義了dispatch()
方法红选,所有上一步調(diào)用self.dispatch()
的時(shí)候會(huì)調(diào)用APIView的dispatch()
APIView
的dispatch()
做了以下5件事
- 請求對象的二次封裝:
request = self.initialize_request(request, *args, **kwargs)
- 請求之前發(fā)起三大認(rèn)證澜公,入口函數(shù)是
self.initial(request, *args, **kwargs)
,認(rèn)證不通過,拋出異常 - 發(fā)出請求:
response = handler(request, *args, **kwargs)
- 請求結(jié)果交給渲染模塊,按照前端要求的渲染類型,返回給前端
elf.response = self.finalize_response(request, response, *args, **kwargs)
- 返回請求結(jié)果
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:
# 請求之前坟乾,做判斷迹辐,認(rèn)證模塊
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)
# 認(rèn)證不通過,拋出異常
except Exception as exc:
response = self.handle_exception(exc)
#finalize_response渲染模塊
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response