DRF请求生命周期解析
- CBV的路由,调用视图的
as_view()
,一般业务视图很少定义自己的as_view()
,所有会找到父类的as_view()
,所以请求走的是APIView
的as_view()
函数 - 在
APIView
的as_view
调父类的(django原生)的as_view
:view = super().as_view(**initkwargs)
,还禁用了csrf
认证,return csrf_exempt(view)
- 在父类的
as_view
中dispatch
方法请求走的又是APIView
的dispatch
- 完成任务方法交给视图类的请求函数处理,得到请求的响应结果,返回给前台
请求步骤拆解
1. 请求进来走的是APIView
的as_view()
函数
APIView
的as_view()
函数主要做了两件事
- 调用父类的
as_view
,就是View
的as_view()
方法 - 禁用
scrf
的token
认证: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(),调用self.dispatch()
完成请求的分发
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):
# 实例化产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返回什么,浏览器就会收到什么
return self.dispatch(request, *args, **kwargs)
# 对象在查找属性或者方法的时候,一定要默念,
# 先从对象这里找,然后从产生对象的类里找,最后从父类里找
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."""
# 这个方法仅仅是在给对象新增属性
self.request = request
self.args = args
self.kwargs = kwargs
3.APIView
类中定义了dispatch()
方法,所有上一步调用self.dispatch()
的时候会调用APIView的dispatch()
APIView
的dispatch()
做了以下5件事
- 请求对象的二次封装:
request = self.initialize_request(request, *args, **kwargs)
- 请求之前发起三大认证,入口函数是
self.initial(request, *args, **kwargs)
,认证不通过,抛出异常 - 发出请求:
response = handler(request, *args, **kwargs)
- 请求结果交给渲染模块,按照前端要求的渲染类型,返回给前端
elf.response = self.finalize_response(request, response, *args, **kwargs)
- 返回请求结果
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)
#finalize_response渲染模块
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
网友评论