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

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

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

 

mixins

     到目前爲止,咱們使用的建立/獲取/更新/刪除操做和咱們建立的任何基於模型的API視圖很是類似。這些常見的行爲是在REST框架的mixin類中實現的github

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

Mixin 類能夠從 rest_framework.mixins導入。json

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

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

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

 

 

 

 

 

 

 

 

使用詳解

1.APIView

APIView對django自己的View進行封裝session

APIView類和通常的View類有如下不一樣:框架

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

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

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

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

                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

相關文章
相關標籤/搜索