1. 程式人生 > >Django REST Framework(DRF)_第二篇

Django REST Framework(DRF)_第二篇

檢視和路由

  1. 檢視封裝

  • 第一次封裝

​ 上一篇最後我們對書籍表做了增刪改查,那麼如果現在我們有幾十上百張表需要這樣做呢?我們知道類的特性有封裝,因此我們可以嘗試進行封裝下.  

from rest_framework.views import APIView
  from rest_framework.response import Response
  
  from .models import Book
  from .modelSerializers import BookModelSerializer
  
  
  class GenericAPIView(APIView):
      queryset = None
      serializer_class = None
  
      def get_queryset(self):
          return self.queryset.all()
  
      def get_serializer(self, *args, **kwargs):
          return self.serializer_class(*args, **kwargs)
  
  
  class ListModelMixin(object):
      # 這個就是把get方法抽離了出來,變成了list方法
      def list(self, request, *args, **kwargs):
          queryset = self.get_queryset()
          serializer_data = self.get_serializer(queryset, many=True)
          return Response(serializer_data.data)
  
  
  class CreateModelMixin(object):
      def create(self,  request, *args, **kwargs):
          ser_obj = self.get_serializer(data=request.data)
          if ser_obj.is_valid():
              ser_obj.save()
              return Response(ser_obj.validated_data)
          return Response(ser_obj.errors)
  
  
  class RetrieveModelMixin(object):
      def retrieve(self, request, id, *args, **kwargs):
          book_obj = self.get_queryset().filter(pk=id).first()
          ser_obj = self.get_serializer(book_obj)
          return Response(ser_obj.data)
  
  
  class UpdateModelMixin(object):
      def update(self, request, id, *args, **kwargs):
          book_obj = self.get_queryset().filter(pk=id).first()
          ser_obj = self.get_serializer(instance=book_obj, data=request.data, partial=True)
          if ser_obj.is_valid():
              ser_obj.save()
              return Response(ser_obj.validated_data)
          return Response(ser_obj.errors)
  
  
  class DestroyModeMixin(object):
      def destroy(self, request, id , *args, **kwargs):
          book_obj = self.get_queryset().filter(id=id).first()
          if not book_obj:
              return Response("要刪除的物件不存在")
          book_obj.delete()
          return Response("")
  
  
  class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
      pass
  
  
  class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModeMixin):
      pass
  
  
  class BookView(ListCreateAPIView):
      """書籍相關檢視"""
      queryset = Book.objects
      serializer_class = BookModelSerializer
  
      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
      serializer_class = BookModelSerializer
  
      def get(self, request, id, *args, **kwargs):
          return self.retrieve(request, id, *args, **kwargs)
  
      def put(self, request,  id, *args, **kwargs):
          return self.update(request, id, *args, **kwargs)
  
      def delete(self, request, id, *args, **kwargs):
          return self.destroy(request, id, *args, **kwargs)

​ 從上面我們可以知道,我們發現檢視中就只有query和ModelSerializer每個方法不一樣,因此,我們先定義了一個GenericAPIView,該方法就是專門獲取query和ModelSerializer.再接著就是將get全部/get單條/post/put/delete方法分別再次封裝到不同類中的方法中,這樣在寫多個表時只需要呼叫方法即可,但是此時你會發現繼承的類有點長,因此又封裝了兩個類專門繼承多個類,這樣我們再寫就只需要繼承一個類了,你看這樣是不是變得更簡單了,我們必須學習這種封裝的思想,使得自己程式碼更加簡潔,複用性更高.

  • 二次最終封裝

    上面封裝似乎已經很好了,但是人總是貪婪的,上面是不是定義了兩個檢視針對不同操作(BookView和BookEditView),那我們想能不能只定義一個檢視就完成呢?此時我們去看一下rest_framework提供的原始碼

    在這裡先被備註下rest_framework給我們的幾個檢視相關的類

    from rest_framework import views        # 裡面主要是有APIView
    from rest_framework import viewsets     # ModelViewSet封裝,繼承mixins下的各個類
    from rest_framework import generics     # GenericAPIView,CreateAPIView等封裝
    from rest_framework import mixins       # list,create,update等方法具體實現

    因為我們需要合併檢視,所以我們需要在as_view()時出入對應關係的引數,所以我們看下viewsets下的ViewSetMixin類, from rest_framework.viewsets import ViewSetMixin.

    從ViewSetMixin中我們可以知道,重寫了as_view()方法,並且將引數傳給了action,這裡我只拿出了一些需要的程式碼進行說明下.

    def as_view(cls, actions=None, **initkwargs):
        # actions = {"get": "list", "post": "create"}
        def view(request, *args, **kwargs):
              for method, action in actions.items():
                    # method = get, action = list
                    handler = getattr(self, action)
                    # 這裡就將self.get = self.list,所以之後get請求就會去找list方法
                    setattr(self, method, handler)
    所以注意下,只有繼承了ViewSetMixin路由才可以傳參,前面說完了,我們來再次封裝下程式碼:

    views下就變成了

    class ModelViewSet(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
        pass
    
    
    class BooksView(ModelViewSet):
        queryset = Book.objects
        serializer_class = BookModelSerializer

    urls下變成

    path('books', views.BooksView.as_view({"get": "list", "post": "create"})),
    re_path('book/(?P<id>\d+)', views.BooksView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),

    其實我們輾轉了大半天,人家早就給我們封裝好了,views下我們啥都不用寫,就只需要繼承人家封裝好的就可以了,就下面幾行程式碼即可

  • DRF檢視最終寫法
    from rest_framework import viewsets 
    class BooksView(viewsets.ModelViewSet):
        queryset = Book.objects
        serializer_class = BookModelSerializer

    但是urls那裡不能寫id了,只能寫pk

    path('books', views.BooksView.as_view({"get": "list", "post": "create"})),
    re_path('book/(?P<pk>\d+)', views.BooksView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),
  1. DRF路由

    思考下,如果我們要寫幾十張表的路由,那豈不是又要每個路由傳一堆一樣的引數?別怕,DRF又幫我們搞定了,我們只需要按照下面的方式寫即可

    from rest_framework.routers import DefaultRouter
    
    # 例項化DefaultRouter
    router = DefaultRouter()
    # 註冊路由以及檢視
    router.register(r"book", views.BooksView)
    
    # 將路由url加入到urlpatterns
    urlpatterns += router.urls

    該方法雖然簡潔快速,但也會暴露很多路由介面,視情況選擇使用.