1. 程式人生 > >Django Rest Framework 檢視和路由

Django Rest Framework 檢視和路由

一、DRF的檢視

(一)APIView

APIView是REST framework提供的所有檢視的基類,繼承自Django的View父類。

APIView與View的不同之處在於:

  • 傳入到檢視方法中的是REST framework的Request物件,而不是Django的HttpRequeset物件;
  • 檢視方法可以返回REST framework的Response物件,檢視會為響應資料設定(render)符合前端要求的格式;
  • 任何APIException異常都會被捕獲到,並且處理成合適的響應資訊;
  • 在進行dispatch()分發前,會對請求進行身份認證、許可權檢查、流量控制。

支援定義的屬性:

  • authentication_classes 列表或元祖,身份認證類
  • permissoin_classes 列表或元祖,許可權檢查類
  • throttle_classes 列表或元祖,流量控制類

我們django中寫CBV的時候繼承的是View,rest_framework繼承的是APIView,那麼他們兩個有什麼不同呢~~~

urlpatterns = [
    url(r'^book$', BookView.as_view()),
    url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
]

我們可以看到,不管是View還是APIView最開始呼叫的都是as_view()方法~~那我們走進原始碼看看~~ 

 

 那我們看看View中的as_view()方法做了什麼~

插一下知識點,super

# 歸併演算法
class A:pass
class B(A):pass
class C(A):pass
class D(B):pass
class E(C):pass
class F(D,E):pass

# 第一步 先找出F類的父類的MRO
# D [D,B,A,O]
# E [E,C,A,O]
# merge([D,B,A,O], [E,C,A,O], [D,E])
# 第一個表的表頭 後面所有的表去掉表頭部分不能含有這個D # FD merge([B,A,O], [E,C,A,O], [E]) # FDB merge([A,O], [E,C,A,O], [E]) # 不滿足條件時 取二個 # FDBE merge([A,O], [C,A,O]) # FDBEC merge([A,O], [A,O]) # FDBECAO print(F.__mro__)

 

 

 我們去initialize_request中看下把什麼賦值給了request,並且賦值給了self.request, 也就是我們在檢視中用的request.xxx到底是什麼~~

我們看到,這個方法返回的是Request這個類的例項物件~~我們注意我們看下這個Request類中的第一個引數request,是我們走我們django的時候的原來的request~

 

 

我們看到了,這個Request類把原來的request賦值給了self._request, 也就是說以後_request是我們老的request,新的request是我們這個Request類~~

那我們繼承APIView之後請求來的資料都在哪呢~~

 

我們用了rest_framework框架以後,

  • request是重新封裝的Request類~
  • request.query_params 存放的是我們get請求的引數
  • request.data 存放的是我們所有的資料,包括post請求的以及put,patch請求~~~

我們寫的檢視可能對多個表進行增刪改查,就導致我們的檢視特別多重複的程式碼~~

那麼我們嘗試著來進行封裝一下~~

原來的view

class BookView(APIView):

    def get(self, request):
        query_set = Book.objects.all()
        book_ser = BookSerializer(query_set, many=True)
        return Response(book_ser.data)

    def post(self, request):
        query_set = request.data
        book_ser = BookSerializer(data=query_set)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class BookEditView(APIView):

    def get(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        book_ser = BookSerializer(query_set)
        return Response(book_ser.data)

    def patch(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        book_ser = BookSerializer(query_set, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)

    def delete(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        if query_set:
            query_set.delete()
            return Response("")
        else:
            return Response("刪除的書籍不存在") 

第一次封裝 

class GenericAPIView(APIView):
    # 幫助我們獲得queryset  serializer_class
    queryset = None
    serializer_class = None
    
    # 獲取queryset
    def get_queryset(self):
        return self.queryset.all()

    # 獲取序列化器
    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin(object):
    # 我們的get方法
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class CreateModelMixin(object):
    # 我們的post方法
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)


class RetrieveModelMixin(object):
    def retrieve(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj)
        return Response(book_ser.data)


class UpdateModelMixin(object):
    def update(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class DestroyModelMixin(object):
    def destroy(self, request, id, *args, **kwargs):
        queryset = self.get_queryset()
        try:
            queryset.get(pk=id).delete()
            return Response("")
        except Exception as e:
            return Response("資訊有誤")
# 我們把公共的部分抽出來 這樣不管寫多少表的增刪改查都變的很簡單
# 這樣封裝後我們的檢視會變成這樣

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id, *args, **kwargs):
        return self.retrieve(request, id, *args, **kwargs)

    def patch(self, request, id, *args, **kwargs):
        return self.update(request, id, *args, **kwargs)

    def destroy(self, request, id, *args, **kwargs):
        return self.delete(request, id, *args, **kwargs)

 

我們封裝的GenericAPIView,包括封裝每個方法的類,其實框架都幫我們封裝好了~~

我們可以直接繼承這些類~~來實現上面的檢視~~可是還有沒有更簡單的方法呢~我們再次封裝一下~~

第二次封裝

 

# 上面我們寫的繼承類太長了~~我們再改改

class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass
    # 方便繼承 什麼也沒做


class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass
    # 方便繼承 什麼也沒做


class BookView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookEditView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id, *args, **kwargs):
        return self.retrieve(request, id, *args, **kwargs)

    def patch(self, request, id, *args, **kwargs):
        return self.update(request, id, *args, **kwargs)

    def delete(self, request, id, *args, **kwargs):
        return self.delete(request, id, *args, **kwargs)

 

 

這次我們只是讓繼承變的簡單了一點而已,好像並沒有什麼大的進步~~

我們可不可以把這兩個檢視合併成一個檢視呢~~~框架給我們提供了一個路由傳參的方法~~

我們看下ViewSetMixin

第三次封裝

urlpatterns = [
    # url(r'^book$', BookView.as_view()),
    # url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
    url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
    url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),
]

路由urls.py

 

 

from rest_framework.viewsets import ViewSetMixin


# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerializer
    
    
# 如果我們再定義一個類
class ModelViewSet(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
    pass


class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer 

 

我們現在的檢視就只要寫兩行就可以了~~~

其實我們寫的所有的檢視~框架都幫我們封裝好了~

注意一點~~用框架封裝的檢視~我們url上的那個關鍵字引數要用pk~~系統預設的~~

奉獻一張圖來看下我們的繼承順序~~~

二、DRF的路由

我們上面的路由傳參寫的特別多~~框架也幫我們封裝好了~

from .views import BookView
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"book", BookView)

urlpatterns = [
    # url(r'^book$', BookView.as_view()),
    # url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
    # url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
    # url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),

]
urlpatterns += router.urls

 

我們可以看到~~通過框架我們可以把路由檢視都變的非常簡單~~

但是需要自定製的時候還是需要我們自己用APIView寫~~當不需要那麼多路由的時候~也不要用這種路由註冊~~

總之~~一切按照業務需要去用~~~