問題:爲什麼前端的請求過來能自動識別Django的CBV模式裏的GET或POST方法
案例:
- views.py
# 視圖層
from django.shortcuts import render, HttpResponse
from django.views import View
class CBVTest(View):
def get(self, request):
return render(request, 'cbv.html')
def post(self, request):
return HttpResponse('cbv post method')
- url.py
# 路由層
from app import views
urlpatterns = [
url(r'^cbv/', views.CBVTest.as_view()), # 注意:這裏的as_view方法是View類中的方法,且加括號運行了
]
CBV源碼分析
流程:
自定義的視圖類(CBVTest)繼承了View,在路由裏CBVTest.as_view()的這個as_view方法是View類的方法,並且及括號執行了,返回結果是在as_view方法中定義的view函數
此時路由就已經是CBVTest.view了,當請求過來觸發view方法,在view方法中又觸發了View類中的dispatch方法
dispatch方法中利用反射獲取CBVTest中的GET或POST方法,做到請求分發的效果
源碼:
View類中的as_view方法:
@classonlymethod
def as_view(cls, **initkwargs):
...
# 定義了view方法
def view(request, *args, **kwargs):
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__
)
# 調用View類中的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=())
# as_view方法將定義的view方法返回
return view
View類中的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)
drf請求源碼分析
案例:
- views.py
# 視圖層
from rest_framework.views import APIView
from rest_framework.response import Response
user_list = [{'id': 1, 'name': 'Bob'}, {'id': 2, 'name': 'Tom'}]
class Users(APIView):
def get(self, request, *args, **kwargs):
return Response({
'status': 0,
'msg': 'ok',
'results': user_list
})
def post(self, request, *args, **kwargs):
# request對formdata,urlencoded,json三個格式參數均能解析
name = request.data.get('name')
id = len(user_list) + 1
user = {'id': id, 'name': name}
user_list.append(user)
return Response({
'status': '0',
'msg': 'ok',
'results': user
})
- urls.py
# 路由層
from app import views
urlpatterns = [
url(r'^users/', views.Users.as_view()),
]
流程:
自定義的視圖類(Users)繼承了APIView,在路由裏Users.as_view()的這個as_view方法是APIView類的方法,並且加括號執行了,在as_view方法裏調用了APIView類的父類(View)裏的as_view方法,並且禁用了csrf
APIView重寫了dispatch方法,所以在as_view方法中調用的是APIView的dispatch方法(完成了很多重要工作),完成任務分發
補充:APIView中的dispatch方法中完成了二次封裝request,認證,權限,頻率功能
源碼:
# as_view()
# 核心走了父類as_view
view = super(APIView, cls).as_view(**initkwargs)
# 返回的是局部禁用csrf認證的view視圖函數
return csrf_exempt(view)
# dispatch(self, request, *args, **kwargs)
# 二次封裝request對象
request = self.initialize_request(request, *args, **kwargs)
# 自定義request規則
self.initial(request, *args, **kwargs)
# initialize_request(self, request, *args, **kwargs)
# 原生request封裝在request._request
# initial(self, request, *args, **kwargs)
# 認證
self.perform_authentication(request)
# 權限
self.check_permissions(request)
# 頻率
self.check_throttles(request)
drf中的request對象
源碼入口
APIView類的dispatch方法中:request = 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)
# 將原生的request傳入Request類
return Request(
request, # 原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# 在Request類中完成了對原生request的二次封裝,使drf的request完全兼容原生request
- 1) drf 對原生request做了二次封裝,request._request就是原生request
- 2) 原生request對象的屬性和方法都可以被drf的request對象直接訪問(兼容)
- 3) drf請求的所有url拼接參數均被解析到query_params中,所有數據包數據都被解析到data中
基於restful規範的drf接口
api應用的子路由: api/url.py
from django.conf.urls import url
from . import views
urlpatterns = [
# 這裏的as_view是APIView類中的,作用是禁用了csrf_exempt,並返回view,調用了View類中的as_view方法
url(r'^test/$',views.Test.as_view())
]
視圖層: views.py
# APIView本質是繼承了View
class Test(APIView):
def get(self, request, *args, **kwargs):
# url 拼接的參數
print(request._request.GET) # 二次封裝request
print(request.GET) # 兼容
print(request.query_params) # 擴展,GET請求拼接的參數這裏都有
return Response('drf get ok')
def post(self, request, *args, **kwargs):
# 請求攜帶的數據包
print(request._request.POST) # 二次封裝方式,沒有json方式的數據
print(request.POST) # 兼容,沒有json方式的數據
print(request.data) # 拓展,兼容性最強,三種數據方式都可以
print(request.query_params) # post拼接的數據也可以接受到
return Response('drf post ok')
url拼接參數 : 只有一種傳參方式,參數都在query_params中
數據包參數 : 有三種傳參方式 form-data,urlencoded,json,參數都在data中