Django Rest framework基礎使用之View:APIView, mixins, generic, viewsets


先看一張圖,對DRF的各個APIView,Mixin,Viewset等有個基本印象:

具體使用方法:

1、APIView:

  DRF 的API視圖 有兩種實現方式:

  一種是基於函數的:@api_view

  一種是基於類的:APIView,APIView是Restframework提供的所有視圖的基類,繼承自Django的View父類

  1)@api_view

    使用@api_view裝飾器,使得我們此處的Request不在是Django標准的HttpRequest,而是restframework的Request。

    默認情況下,只有GET請求會被接收,它也允許我們自己配置函數允許接收的請求類型

    @api_view():默認只接收GET請求

    @api_view(http_method_names=['GET']):指明接收的請求類型

    @api_view(['GET', 'POST']):接收get post請求

    @api_view(['GET','POST'])

    def Snippet_list(request):

      if request.method=="GET":

        pass

      elif request.method == "POST":

        pass

  2)APIView

    APIView是基於類的裝飾器,顯然,類視圖更符合面向對象的原則,它支持GET POST PUT DELETE等請求類型,且各種類型的請求之間,有了更好的分離

    在進行dispatch分發之前,會對請求進行身份認證,權限檢查,流量控制。

    支持定義的屬性:

      authentication_classes=():列表或元組,身份認證類

      permission_classes=():列表或元組,權限檢查類

      throttle_classes=():列表或元組,流量控制類              

    class Snippet_list(APIView):

      def dispatch(self, request, *args, **kwargs):

        # 請求到來之后,都需要執行dispatch方法,dispatch根據請求方式的不同,分別觸發對應的get post, put等方法

        return super().dispatch(request, *args, **kwargs)

      def get(self, request, pk, format=None):

        snippet = Snippets.objects.get(pk=pk)

        serializer = SnippetSerializer(snippet, many=True)

        return Response(serialzer.data)

      def post(self, request):

        serializer = SnippetSerializer(data = request.data)

        if serializer.is_valid():

          serializer.save()

          return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

      def delete(self, request, pk, format=None):

        snippet = Snippets.object.get(pk=pk)

        snippet.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

    配置url:

    urlpatterns = [

      url(r"^snippets/(?P<pk>[0-9]+)$", Snippet_list.as_view()),

    ]

2、mixins

  使用基於類的視圖的一個最大的好處就是我們可以靈活的選擇各種View,使我們的開發更加的簡潔

  mixins里面對應了ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

  這些Mixin方法,對我們上面APIView中重寫的GET POST PUT DELETE進行了封裝,使得我們的代碼更加簡潔

  

  下面是使用mixin的例子:

  GenericAPIView繼承了APIView,通過GenericViewSet來構建我們的視圖,然后組合ListModelMixin和CreateModelMixin來實現我們上面的代碼功能

  基類提供了核心的功能,ListModelMixin和CreateModelMixin提供了.list()和.create()函數,下面我們將get和post與.list和.create綁定起來

  class snippet_list(mixins.ListModelMixin, generic.GenericAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):

      return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):

      return self.create(request, *args, **kwargs)

 

3、使用generic

  我們上面使用mixin的代碼,已經非常簡潔了,但是restframework給我們提供了一組混合的generic視圖,可以使我們的代碼 大大簡化

  GenericAPIView:繼承自APIView,增加了對列表視圖或者詳情視圖可能用到的通用支持方法。通常使用時,可搭配一個或者多個Mixin擴展類

  支持定義的屬性:

    列表視圖與詳情視圖通用:queryset(視圖的查詢集), serializer_class(視圖使用的序列化器)

    列表視圖專用:pagination_classes(分頁控制類),filter_backends(過濾控制)

    詳情頁視圖專用:lookup_field(查詢單一數據庫對象時使用的條件字段,默認為pk)

  提供的方法:

    通用:

      1)get_queryset(self):返回視圖使用的查詢集,是獲取數據的基礎,默認返回queryset,可以重寫:

      def get_queryset(self): 

        user = self.request.user

        return user.accounts.all()

      2)get_serializer_class(self):返回序列化器,默認返回serializer_class,可以重寫,如:

        def get_serializer_class(self):

          if self.request.user.is_staff:

            return AllAccountSerializer

          return BasicAccountSerializer

      3)get_serializer(self, *args, **kwargs):返回序列化器對象

    詳情視圖使用:

      get_object(self):返回詳情視圖所需的模型類數據對象,默認使用lookup_field參數來過濾queryset

      該方法默認使用APIView提供的check_object_permissions方法檢查當前對象是否有權限被訪問

      class SnippetDetail(GenericAPIView):

        queryset = Snippets.objects.all()

        seriazlier_class = SnippetSerializer

        def get(self, request, pk):

          snippet = self.get_object()

          serializer = self.get_serializer(snippet)

          return Response(serialzier.data)

      url:url(r"^snippet/?P<pk>[0-9]+/$", SnippetDetail.as_view())

  五個擴展類:generics.ListAPIView, generics.CreateAPIView, generic.RetrieveAPIView, generic.UpdateAPIView, generic.DestroyAPIView,它們會繼承GenericAPIView和mixin對應的視圖

  

  上面可以看到generics.ListAPIView里面做的事情,它替我們繼承了mixins.ListModelMixin和GenericAPIView,還重寫了get方法,所以我們直接繼承generic.ListAPIView,代碼就會更簡潔,

  只需要設置一下queryset和serializer_class即可,如下:

  class snippet_list(generics.ListAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

 

4、視圖集ViewSet

  使用視圖集ViewSet,可以將一系列邏輯相關的動作放到一個類中。

    list():提供一組數據

    retrieve():提供單個數據

    create():創建數據

    destroy():刪除數據

    update():修改數據

  ViewSet主要是和路由搭配使用,將我們通常使用的get, post等和方法和ViewSet實現的action如list(), create()等對應上。

  action屬性:

    在視圖集中,我們可以通過action對象屬性來確定當前請求視圖集時的action動作是哪個

    def get_serializer_class(self):

      if self.action == "create":

        return SnippetCreateSerializer

      else:

        return SnippetListSerializer

  url配置:

    urlpatterns = [

      url(r"^snippet/$", SnippetViewSet.as_view({"get":"list", "post":"create"})),

      url(r"^snippet/?P<pk>[0-9]+/$", SnippetViewSet.as_view({"get":"retrive"})),

    ]

    上面url配置,是我們手動的將請求方式和action動作對應起來,其實restframework也已經幫我對應好了,它是通過Router模塊來完成的,我們只需要使用router將對應的視圖注冊即可。

    from rest_framework.routers import DefaultRouter

    router = DefaultRouter() 

    router.register(r"snippet", SnippetViewSet)

    urlpatterns = [

      url(r"^", include(router.urls))

    ]

 5、Django restframework 過濾器

  如過濾文章閱讀量小於100的:

  class ArticleViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):

    def get_queryset(self):

      read_nums = self.query_params.get("readNums", 0)

      if read_nums:

        return Article.objects.filter(read_nums__lt=int(read_nums))

      return Article.objects.all()

  上面這種方式,如果過濾字段少的話,可以方便使用,但是如果過濾字段多,那代碼量就會很繁復了。如果還想要用簡潔的代碼實現大量的過濾邏輯,就需要用到其他方法,如django-filter

  pip install django-filter,並將django-filter加入到INSTALLED_APPS中。

  如:

  from django_filters.rest_framework import DjangoFilterBackend

  class ArticleViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):

    queryset = Article.objects.all()

    serializer_class = ArticleSerialzier

    filter_backends = (DjangoFilterBackend, )

    filter_fields = ("name", "title")

   這種寫法相對於上面第一種寫法就簡單很多了,但是如果我們想要查詢閱讀數量大於10,小於100的文章要怎么辦呢?所以這種寫法還是有他的局限性,要實現這種情況的過濾,需要我們自定義過濾器:

  新建filters.py

  import django_filters

  class ArticleFilter(django_filters.rest_framework.FilterSet):

    title= django_filters.CharFilter(lookup_expr='icontains')

    readNums = django_filters.NumberFilter(lookup_expr="gte")

    class Meta:

      model = Article

      fields = ["min_readers","readNums", "clickNums"]

    說明:

      上面:model:Article---指出該類是為Article 這個model定義的過濾類

           lookup_expr------指出過濾條件,是包含,還是大於或是其他,后面會想起說明

           fields----------------指定了支持過濾的字段,如果fields中指定了字段,但是沒有指定過濾方法,則表示精確查詢

  view中使用:

    class SnippetViewset(mixins.ListModelMixin, viewsets.GenericViewSet):

      queryset = Article.objects.all()

      serializer_class = ArticleSerializer

      filter_backends = (DjangoFilterBackends, )

      filter_class = ArticleFilter

  

  多表過濾:                

    class Book(model.Model):

      name = model.CharField(max_length = 50)

    class Author(model.Model):

      name = model.CharField(max_length=20)

      book = model.ForeignKey(Book)

    例如上面的作者和書是一對多的關系,一個人可以寫多本書。那么我們想要通過書本的名字去查詢它的作者,要怎么做呢?

    class AuthorFilter(django_filters.rest_framework.FilterSet):

      book__name = django_filters.CharFilter(lookup_expr='icontains')

      class Meta:

        model = Author

        fields = ["book__name"]

    在View中使用:

      按照上面的單表查詢的View寫法也是可以的,這里我們通過另一種重寫list的方式來完成,旨在說明自定義分頁的寫法。

      class AuthorViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):

        queryset = Author.objects.all()

        serializer = AuthorSerializer

        def list(self, request, *args, **kwargs):

          object = AuthorFilter(request.query_params, queryset=self.queryset)

          # 過濾后查詢出的數據

          filterRecords = object.qs.count()

          # 前端傳過來的參數:當前第幾頁

          curP = int(request.query_params.get("p", 0))

          # 前端傳過來的參數:每頁有幾條數據

          size = int(request.query_params.get("s", 0))

          if curP and size:

            start = (curP-1)*size

            length = size

          else:

            start = 0

            length = filterRecords

          # 通過調用過濾器的返回值的qs來處理自定義分頁,有興趣的話可以去django.db.model.query.py中去看看

          object = object.qs[start:(start+length)]

          serializer = self.get_serializer(object, many = True)

          return Response(serializer.data)

    

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM