django rest framework 檢視原始碼解析

weixin_34129145發表於2018-11-04

檢視原始碼解析

9505682-80cb43f880366a51
image
9505682-e715d1f88c97d377
image

1) CreateAPIView
提供 post 方法
繼承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
繼承自:GenericAPIView、ListModelMixin
3)RetireveAPIView
提供 get 方法
繼承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
繼承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
繼承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
繼承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
繼承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

下面就一個個來介紹:

Mixins的五個類:

首先點開ModelViewSet類:

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):

點進第一個類中:

CreateModelMixin:
class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

這是CreateModelMixin類下的原始碼,首先通過request獲取請求得到的資訊傳給引數serializer,對它進行使用者Token驗證,如果使用者通過,將其儲存並且通過return {‘Location’: str(data[api_settings.URL_FIELD_NAME])}儲存一個臨時頭部資料,最後將序列化的資料,響應狀態碼(201 Created:請求已經被實現)、頭部一併返回即可。
另外補充,get_serializer方法是繼承自GenericAPIView類的序列化資料。

RetrieveModelMixin:
class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

這是RetrieveModelMixin下的原始碼,首先同樣get_object和get_serializer是繼承自GenericAPIView類中的方法,這裡需要注意一下get_object的這條語句:

lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

因為這個函式後面部分都是對lookup_url_kwarg這個引數進行處理,而給這個引數賦值的兩個,前者在最前面的初始引數是None,所以我們又跳到後者lookup_field,發現它的預設值是"pk",所以這也是為什麼我們之前只要是精確查詢路由部分正則後的引數都設為pk的原因。

  def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

總結:返回詳情檢視所需的模型類資料物件,預設使用lookup_field引數來過濾queryset。 在檢視中可以呼叫該方法獲取詳情資訊的模型類物件。若詳情訪問的模型類物件不存在,會返回404。該方法會預設使用APIView提供的check_object_permissions方法檢查當前物件是否有許可權被訪問。

  1. lookup_field 查詢單一資料庫物件時使用的條件欄位,預設為’pk’
  2. lookup_url_kwarg 查詢單一資料時URL中的引數關鍵字名稱,預設與look_field相同
UpdateModelMixin
class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

和前面的create類似,意思通過instance = self.get_object()獲取要修改的資料,然後get_serializer拿到序列化器、進行請求資料合法校驗、然後通過Token進行使用者驗證、然後將更新好的資料返回return Response(serializer.data)

DestroyModelMixin
class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

通過pk(或者其他標識)獲取要刪除的資料,然後instance.delete()刪除,最後返回return Response(status=status.HTTP_204_NO_CONTENT)
HTTP 204(no content)表示響應執行成功,但沒有資料返回,瀏覽器不用重新整理。這個除了呼叫get_object方法獲取資料,其它都在類範圍內。

ListModelMixin
class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

queryset = self.filter_queryset(self.get_queryset()),ListModelMixin類list方法獲取了這個queryset,同樣該方法是在GenericAPIView類中,然後在判斷是否分頁,最後獲取self.get_serializer(queryset, many=True)序列化類,最後將序列化後的結果serializer.data作為返回return Response(serializer.data)返回即可。這裡就不再分析中間這段分頁的含義了,分頁也算一個元件,如果以後有時間,可以再開一貼分析一下,我們只要看懂前面和後面就行了。

五個類小結

其實我們發現上面這五個類進行的功能並不多,只是說幫我們省略了邏輯處理部分,還有解決了一些零零碎碎的小問題,那麼我們定義的路由,以及檢視部分定義的類,怎樣才能讓django識別到,並且既然是CBV,那麼就少不了as_view()方法,那麼它在哪,下面就讓我們進入GenericViewSet下

GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

直接進入繼承類。

ViewSetMixin

我們先進入ViewSetMixin模組類中,發現它類下的第一個便是as_view方法,然後再進一步可以看到view函式,我們此處需要的就是它:

def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    # We also store the mapping of request methods to actions,
    # so that we can later set the action attribute.
    # eg. `self.action = 'list'` on an incoming GET request.
    self.action_map = actions

    # Bind methods to actions
    # This is the bit that's different to a standard view
    for method, action in actions.items():
        handler = getattr(self, action)
        setattr(self, method, handler)

    if hasattr(self, 'get') and not hasattr(self, 'head'):
        self.head = self.get

    self.request = request
    self.args = args
    self.kwargs = kwargs

    # And continue as usual
    return self.dispatch(request, *args, **kwargs)
 for method, action in actions.items():
        handler = getattr(self, action)
        setattr(self, method, handler)

這一段的意思是遍歷actions裡的資料,method接收的是字典的鍵,而action接收的是值,actions就是我們在url傳遞引數,然後通過getattr反射將action的值給handler,最後執行setattr,這個方法的意思是給物件的屬性賦值,若屬性不存在,先建立再賦值。也就是說將method方法裡的替換成handler。

APIView

之後找到我們想要定義的dispatch方法的回撥函式裡,這個是在APIView模組類中,並且算是重寫了dispatch方法,如果還想刨根揭底的話,就會發現它後面還有一個dispatch方法,是View模組下的,這個可以看上面我畫的流程圖。所以dispatch方法的返回值,就是view的返回值,view的返回值就是as_view的返回值,那麼我們整個原始碼邏輯就都通了。

相關文章