REST framework 請求模塊

問題:爲什麼前端的請求過來能自動識別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中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章