Django Rest Framework(3)-----APIView與Viewsets


REST framework提供了一個APIView類,它是Django的View類的子類。

REST framework主要的幾種view以及他們之間的關系:

 

mixins

     到目前為止,我們使用的創建/獲取/更新/刪除操作和我們創建的任何基於模型的API視圖非常相似。這些常見的行為是在REST框架的mixin類中實現的

Mixin 類提供用於提供基本視圖行為的操作。注意mixin類提供動作方法,而不是直接定義處理程序方法,例如 .get() 和 .post(), 這允許更靈活的行為組成。

Mixin 類可以從 rest_framework.mixins導入。

mixins 作用 對應HTTP的請求方法
mixins.ListModelMixin

定義list方法,返回一個queryset的列表

GET
mixins.CreateModelMixin 定義create方法,創建一個實例 POST
mixins.RetrieveModelMixin 定義retrieve方法,返回一個具體的實例 GET
mixins.UpdateModelMixin 定義update方法,對某個實例進行更新 PUT/PATCH
mixins.DestroyModelMixin 定義delete方法,刪除某個實例 DELETE

 

 

 

 

 

 

 

 

使用詳解

1.APIView

APIView對django本身的View進行封裝

APIView類和一般的View類有以下不同:

  • 被傳入到處理方法的請求不會是Django的HttpRequest類的實例,而是REST framework的Request類的實例。

  • 處理方法可以返回REST framework的Response,而不是Django的HttpRequest。視圖會管理內容協議,給響應設置正確的渲染器。

  • 任何APIException異常都會被捕獲,並且傳遞給合適的響應。

  • 進入的請求將會經過認證,合適的權限和(或)節流檢查會在請求被派發到處理方法之前   

                authentication_classes:用戶登錄認證方式,session或者token等等。

                permission_classes:權限設置,是否需要登錄等。

                throttle_classes:限速設置,對用戶進行一定的訪問次數限制等等

使用APIView類和使用一般的View類非常相似,通常,進入的請求會被分發到合適處理方法比如.get(),或者.post。另外,很多屬性會被設定在控制API策略的各種切面的類上。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions

class ListUsers(APIView):
    """
    列出系統中的所有用戶的視圖。

    * 需要token認證
    * 只有管理員用戶可以訪問這個視圖。
    """
    authentication_classes = (authentication.TokenAuthentication,)
    permission_classes = (permissions.IsAdminUser,)

    def get(self, request, format=None):
        """
        Return a list of all users.
        """
        usernames = [user.username for user in User.objects.all()]
        return Response(usernames)

 

2.GenericAPIView

  該類對APIView進行更高層次的封裝,擴展了REST框架的 APIView 類,為標准list和detail view 添加了通常需要的行為。

  提供的每個具體通用視圖是通過將 GenericAPIView 與一個或多個mixin類組合來構建的。

from rest_framework import mixins
from rest_framework import generics
class CourseListView(mixins.ListModelMixin, generics.GenericAPIView):
    """
    課程列表頁
    """
    queryset = Course.objects.all()
    serialize_class = CourseSerializer
    def get(self, request, *args, **kwargs):
    # list方法是存在於mixins中的,同理,create等等也是
    # GenericAPIView沒有這些方法!
        return self.list(request, *args, **kwargs)

上述中,繼承了mixins中的ListModelMixin,就是對應把HTTP的get方法轉換調用list方法,list方法會返回queryset的json數據

GenericAPIView對APIView再次封裝,實現了強大功能

  • 加入queryset屬性,可以直接設置這個屬性,不必再將實例化的data,再次傳給seriliazer,系統會自動檢測到。除此之外,可以重載get_queryset(),這樣就不必設置’queryset=*’,這樣就變得更加靈活,可以進行完全的自定義。
  • 加入serializer_class屬性與實現get_serializer_class()方法。兩者的存在一個即可,通過這個,在返回時,不必去指定某個serializer。
  • 設置過濾器模板:filter_backends。
  • 設置分頁模板:pagination_class。
  • 加入 lookup_field=”pk”,以及實現了get_object方法,這個用得場景不多,但十分重要。它們兩者的關系同1,要么設置屬性,要么重載方法。它們的功能在於獲取某一個實例時,指定傳進來的后綴是什么。

      例如·,獲取具體的某個課程,假設傳進來的URL為:http://127.0.0.1:8000/course/1/,系統會默認這個1指的是course的id。那么,現在面臨一個問題,假設我定義了一個用戶收藏的Model,我想要知道我id為1的主機是否收藏了,我傳進來的URL為:http://127.0.0.1:8000/userfav/1/,系統會默認獲取userfav的id=1的實例,這個邏輯明顯是錯的,我們需要獲取course的id=1的收藏記錄,所以我們就需要用到這個屬性或者重載lookup_field=”course_id”這個方法。

GenericAPIView的不足之處:

  既然GenericAPIView以及它相關的View已經完成了許許多多的功能,那么還要ViewSet干嘛!
首先,我們思考一個問題,同樣上面的例子,我們在功能上,要獲取課程的列表,也要獲取某個課程的具體信息。那么怎么實現,按照GenericAPIView,我們可以這樣實現:

 

class CourseView(ListAPIView,RetrieveAPIView):
    # 只需要在上面的基礎上,再繼承RetrieveAPIView就ok了。
    queryset = Course.objects.all()
    serialize_class = CourseSerializer

 

  但這樣實現有一個問題,關於serialize_class,顯然,當獲取課程列表時,只需要傳回去所有課程的簡要信息,如課程名字,老師,封面等等,但當獲取課程的具體信息,我們還要將他們的章節以及相關下載資料(很明顯,章節是另外一個model,有一個外鍵指向course),這些信息會很多,在獲取課程列表,將這些傳回去顯然是不理智的。那么,還需要再定義一個CourseDetailSerializer,在get /courses/的時候,使用CourseSerializer,在get /courses/id/的時候,使用CourseDetailSerializer。
  那么,問題來了,我們怎么獲取到是哪個action方法?這個時候,viewset就出場了!

3.Viewset

GenericViewSet繼承了GenericAPIView,依然有get_queryset,get_serialize_class相關屬性與方法,GenericViewSet重寫了as_view方法,可以獲取到HTTP的請求方法。 

使用ViewSet類比使用View類有兩個主要優點。

  • 重復邏輯可以組合成一個類。在上面的示例中,我們只需要指定queryset一次,它將在多個視圖中使用。
  • 通過使用路由器,我們不再需要處理自己的URL連接。

如:上述問題中我們可以這樣解決

法一:

from rest_framework import viewsets
import...
class CourseViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    queryset = Course.objects.all()
    
    def get_serializer_class(self):
    # 重寫get_serializer_class方法
        if self.action == 'list':
            return CourseSerializer
        return CourseDetailSerializer

http請求方法與mixins的方法進行綁定

  但GenericViewSet本身依然不存在list, create方法,需要我們與mixins一起混合使用,那么新問題來了?我們依然需要自己寫get、post方法,然后再return list或者create等方法嗎?當然不!重寫as_view的方法為我們提供了綁定的功能,我們在設置url的時候:
# 進行綁定
courses = CourseViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
urlpatterns = [
    ...
    # 常規加入url匹配項
    url(r'courses/', CourseViewSet.as_view(), name='courses')]

這樣,我們就將http請求方法與mixins方法進行了關聯。那么還有更簡潔的方法嗎?很明顯,當然有,這個時候,route就登場了!

法二:route方法注冊與綁定

  因為我們使用ViewSet類而不是View類,實際上不用自己設計URL conf及綁定HTTP方法。連接resources到views和urls的約定可以使用Router類自動處理。我們需要做的僅僅是正確的注冊View到Router中,然后讓它執行其余操作。新的urls.py代碼如下:

 

from rest_framework.routers import DefaultRouter
router = DefaultRouter() # 只需要實現一次
router.register(r'courses', CourseViewSet, base_name='courses')
urlpatterns = [
    ...
    # 只需要加入一次
    url(r'^', include(router.urls)),]

  route中使用的一定要是ViewSet,用router.register的方法注冊url不僅可以很好的管理url,不會導致url過多而混亂,而且還能實現http方法與mixins中的相關方法進行連接。

 

ModelViewSet:

  在viewset中,還提供了兩個以及與mixins綁定好的ViewSet。當然,這兩個ViewSet完全可以自己實現,它只是把各類mixins與GenericViewSet繼承在一起了:
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                           mixins.ListModelMixin,
                           GenericViewSet):
    # 滿足只有GET方法請求的情景
    pass
    
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    # 滿足所有請求都有的情景
    pass

      在開發的時候,直接繼承 viewsets.ModelViewSet 就可以了,這樣就不用寫list、create、update等方法了,當然具體問題具體分析,如果你的需求與DRF提供的不一致,那么你就可以重寫相應的方法即可

假如有這樣一個需求,你可能需要過濾查詢集,以確保只返回與當前通過身份驗證的用戶發出的請求相關的結果。

class CourserViewSet(ModelViewSet):
    """
    每個用戶只可以查看owner屬於自己的條目,可以創建條目
    """

    def list(self, request, *args, **kwargs):
        self.queryset = Course.objects.filter(owner=request.user.id)
        self.serializer_class = CourseSerializer
        return super(CourserViewSet, self).list(request, *args, **kwargs)

    def create(self, request, format=None):
        serializer = CourseSerializer(data=request.data)
        if serializer.is_valid():
            # .save()是調用CourseSerializer中的create()方法
            serializer.save(owner=self.request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

     這里定義了兩個方法,list方式中我們我們重寫了queryset(就是對返回結果做了一次過濾),然后對於serializer_class指定了一個序列化類。並且我們使用super方法,繼續載入viewset中的list方法,這里只會覆蓋原有list的queryset和serializer_class。而對於create方法,我們則是完全重寫了原viewset中的create方法。這里只是演示一下再ViewSet模式下如何來做工作。原則就是能用ViewSet內置的就是用內置的,內置的不滿足需求就可以重寫部分或全部重寫。

另外,如果只是過濾查詢集,最簡單方法是重寫.get_queryset()方法即可。重寫此方法允許你以多種不同方式自定義視圖返回的查詢集。

class CourseViewSet(ModelViewSet):
    """
    每個用戶只可以查看owner屬於自己的條目,可以創建條目
    """
    
    serializer_class = CourseSerializer

    def list(self, request, *args, **kwargs):
        return Course.objects.filter(owner=request.user.id)

 

自定義ViewSet基類

要創建基礎視圖集類,提供createlistretrieve操作,繼承GenericViewSet和混入所需的操作:
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
                                mixins.ListModelMixin,
                                mixins.RetrieveModelMixin,
                                viewsets.GenericViewSet):
    """
    A viewset that provides `retrieve`, `create`, and `list` actions.

    To use it, override the class and set the `.queryset` and
    `.serializer_class` attributes.
    """
    pass

ps:在開發時是使用ViewSet與mixins方法結合進行可以為我們節省很多功夫

 

參考:

https://q1mi.github.io/Django-REST-framework-documentation/api-guide/generic-views_zh/

https://www.django-rest-framework.org/api-guide/viewsets/

http://www.ywnds.com/?p=14104


免責聲明!

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



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