前言
一般我們寫完序列化以後,我們就會開始寫檢視了,drf
中我們一般使用CBV
的方式,也就是類檢視的方式,最基礎的我們會使用from rest_framework.views import APIView
,APIView
繼承自View
,關於檢視的詳解,我們後續再細講。本章介紹drf
的請求生命週期
前置準備工作
我們先寫一個檢視類TestView
,程式碼如下:
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def get(self, request, *args, **kwargs):
return Response("drf get ok")
def post(self, request, *args, **kwargs):
return Response("drf post ok")
注意:這裡的Response
必須是drf下的Response
,不能是Django原生的HttpResponse
或者是JsonResponse
,否則會出錯
接著,在urls.py
中配置路由,如下
urlpatterns = [
path('test/', views.TestView.as_view(), name="Test"),
]
然後我們訪問http://127.0.0.1:8000/drf/test/
,會出現下圖樣式,代表請求成功
接著我們在介面工具中使用POST
請求方式訪問,返回結果如下:
"drf post ok"
以上2種訪問方式都成功了,接下來我們分析其中的請求過程以及原理
請求生命週期分析
首先我們先從路由配置中看到views.TestView.as_view()
,呼叫的是TestView
類檢視下的as_view
方法,但是我們上面定義該方法的時候,沒有重寫as_view()方法
,所以會呼叫父類APIView
中的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.
"""
# 判斷queryset是否是QuerySet物件
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
# 呼叫父類的as_view方法
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.
# 禁用了csrf認證
return csrf_exempt(view)
通過這行程式碼view = super().as_view(**initkwargs)
,可以知道APIView
的as_view
方法也呼叫了父類View
的as_view
方法,原始碼如下:
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):
self = cls(**initkwargs)
# 如果有get屬性,沒有head屬性,那麼head就是get
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
# 初始化所有檢視方法共享的屬性
self.setup(request, *args, **kwargs)
# 如果沒有request屬性,報異常
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
as_view
方法返回的是view
,view
返回的是dispatch
方法,dispatch
方法也是呼叫的APIView
下的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物件
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
# 獲取request的請求方法
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)
# 返回一個response響應物件
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
dispatch
返回一個response
響應物件,得到請求的響應結果,返回給前臺
總結
- url請求走的是
APIView
的as_view
函式 - 在
APIView
的as_view
呼叫父類(django原生)的as_view
,還禁用了csrf
認證 - 在父類的
as_view
中的dispatch
方法請求走的又是APIView
的dispatch
- 完成任務方法交給檢視類函式處理,得到請求的響應結果,返回給前臺