Django REST framework API 指南(5):檢視集

wcode發表於2019-03-04

Django REST framework API 指南(1):請求
Django REST framework API 指南(2):響應
Django REST framework API 指南(3):檢視
Django REST framework API 指南(4):通用檢視
Django REST framework API 指南(5):檢視集
Django REST framework API 指南(6):路由
Django REST framework API 指南(7):解析

官方原文連結

檢視集

在路由決定了哪個控制器用於請求後,控制器負責理解請求併產生適當的輸出。
Ruby on Rails 文件

Django REST framework 允許將一組相關檢視的邏輯組合到一個稱為 ViewSet 的類中。在其他框架中,您可能會發現概念上類似的實現,名為 “Resources” 或 “Controllers” 。

ViewSet 類只是一種基於類的 View,它不提供任何處理方法,如 .get().post(),而是提供諸如 .list().create() 之類的操作。

ViewSet 只在用 .as_view() 方法繫結到最終化檢視時做一些相應操作。

通常,不是在 urlconf 中的檢視集中明確註冊檢視,而是使用路由器類註冊檢視集,這會自動為您確定 urlconf。

舉個例子

定義一個簡單的檢視集,可以用來列出或檢索系統中的所有使用者。

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response

class UserViewSet(viewsets.ViewSet):

    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
複製程式碼

如果需要,可以將這個檢視集合成兩個單獨的檢視,如下所示:

user_list = UserViewSet.as_view({`get`: `list`})
user_detail = UserViewSet.as_view({`get`: `retrieve`})
複製程式碼

通常情況下,我們不會這樣做,而是用路由器註冊檢視集,並允許自動生成 urlconf。

from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r`users`, UserViewSet, base_name=`user`)
urlpatterns = router.urls
複製程式碼

不用自己編寫檢視集,通常使用預設提供的現有基類。例如:

class UserViewSet(viewsets.ModelViewSet):
    """
    用於檢視和編輯使用者例項的檢視。
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()
複製程式碼

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

  • 重複的邏輯可以合併成一個類。在上面的例子中,我們只需要指定一次查詢集,它將在多個檢視中使用。
  • 通過使用 routers,我們不再需要處理自己的 URL 配置。

這兩者各有優缺點。使用常規檢視和 URL 配置檔案更加明確,併為您提供更多控制。如果想要更快速的開發出一個應用,或者需要使大型 API 的 URL 配置始終保持一致,檢視集會非常有用。

操作檢視集

REST framework 中包含的預設 routes 將為一組標準的 create / retrieve / update / destroy 風格 action 提供路由,如下所示:

class UserViewSet(viewsets.ViewSet):
    """
    這些方法將由路由器負責處理。

    如果要使用字尾,請確保加上 `format = None` 關鍵字引數
    """

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass
複製程式碼

在排程期間,當前 action 的名稱可以通過 .action 屬性獲得。您可以檢查 .action 以根據當前 action 調整行為。

例如,您可以將許可權限制為只有 admin 才能訪問 list 以外的其他 action,如下所示:

def get_permissions(self):
    """
    例項化並返回此檢視所需的許可權列表。
    """
    if self.action == `list`:
        permission_classes = [IsAuthenticated]
    else:
        permission_classes = [IsAdmin]
    return [permission() for permission in permission_classes]
複製程式碼

標記額外的路由行為

如果需要路由特定方法,則可以用 @detail_route@list_route 裝飾器進行修飾。

@detail_route 裝飾器在其 URL 模式中包含 pk,用於支援需要獲取單個例項的方法。@list_route 修飾器適用於在物件列表上操作的方法。

舉個例子:

from django.contrib.auth.models import User
from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    提供標準操作的檢視集
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @detail_route(methods=[`post`])
    def set_password(self, request, pk=None):
        user = self.get_object()
        serializer = PasswordSerializer(data=request.data)
        if serializer.is_valid():
            user.set_password(serializer.data[`password`])
            user.save()
            return Response({`status`: `password set`})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @list_route()
    def recent_users(self, request):
        recent_users = User.objects.all().order(`-last_login`)

        page = self.paginate_queryset(recent_users)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(recent_users, many=True)
        return Response(serializer.data)
複製程式碼

另外,裝飾器可以為路由檢視設定額外的引數。例如…

    @detail_route(methods=[`post`], permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
       ...
複製程式碼

這些裝飾器預設路由 GET 請求,但也可以使用 methods 引數接受其他 HTTP 方法。例如:

    @detail_route(methods=[`post`, `delete`])
    def unset_password(self, request, pk=None):
       ...
複製程式碼

這兩個新操作將在 urls ^users/{pk}/set_password/$^users/{pk}/unset_password/$ 上。

action 跳轉

如果你需要獲取 action 的 URL ,請使用 .reverse_action() 方法。這是 .reverse() 的一個便捷包裝,它會自動傳遞檢視的請求物件,並將 url_name.basename 屬性掛接。

請注意,basename 是在 ViewSet 註冊過程中由路由器提供的。如果您不使用路由器,則必須提供.as_view() 方法的 basename 引數。

使用上一節中的示例:

>>> view.reverse_action(`set-password`, args=[`1`])
`http://localhost:8000/api/users/1/set_password`
複製程式碼

url_name 引數應該與 @list_route@detail_route 裝飾器的相同引數匹配。另外,這可以用來反轉預設 listdetail 路由。

API 參考

ViewSet

ViewSet 類繼承自 APIView。您可以使用任何標準屬性(如 permission_classesauthentication_classes)來控制檢視上的 API 策略。

ViewSet 類不提供任何 action 的實現。為了使用 ViewSet 類,必須繼承該類並明確定義 action 實現。

GenericViewSet

GenericViewSet 類繼承自 GenericAPIView,並提供預設的 get_objectget_queryset 方法和其他通用檢視基礎行為,但預設情況下不包含任何操作。

為了使用 GenericViewSet 類,必須繼承該類並混合所需的 mixin 類,或明確定義操作實現。

ModelViewSet

ModelViewSet 類繼承自 GenericAPIView,並通過混合各種 mixin 類的行為來包含各種操作的實現。

ModelViewSet 提供的操作有 .list() , .retrieve() , .create() , .update() , .partial_update(), 和 .destroy()

舉個例子:

由於 ModelViewSet 類繼承自 GenericAPIView,因此通常需要提供至少 querysetserializer_class 屬性。例如:

class AccountViewSet(viewsets.ModelViewSet):
    """
    用於檢視和編輯 Account
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]
複製程式碼

請注意,您可以覆蓋 GenericAPIView 提供的任何標準屬性或方法。例如,要動態確定它應該操作的查詢集的ViewSet,可以這樣做:

class AccountViewSet(viewsets.ModelViewSet):
    """
    A simple ViewSet for viewing and editing the accounts
    associated with the user.
    """
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

    def get_queryset(self):
        return self.request.user.accounts.all()
複製程式碼

但請注意,從 ViewSet 中刪除 queryset 屬性後,任何關聯的 router 將無法自動匯出模型的 base_name,因此您必須將 base_name kwarg 指定為 router 註冊的一部分。

還要注意,雖然這個類預設提供了完整的 create / list / retrieve / update / destroy 操作集,但您可以通過使用標準許可權類來限制可用操作。

ReadOnlyModelViewSet

ReadOnlyModelViewSet 類也從 GenericAPIView 繼承。與 ModelViewSet 一樣,它也包含各種操作的實現,但與 ModelViewSet 不同的是它只提供 “只讀” 操作,.list().retrieve()

舉個例子:

ModelViewSet 一樣,您通常需要提供至少 querysetserializer_class 屬性。例如:

class AccountViewSet(viewsets.ReadOnlyModelViewSet):
    """
    A simple ViewSet for viewing accounts.
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
複製程式碼

同樣,與 ModelViewSet 一樣,您可以覆蓋GenericAPIView 可用的任何標準屬性和方法。

自定義檢視集基類

您可能需要使用沒有完整 ModelViewSet 操作集的自定義 ViewSet 類,或其他自定義行為。

舉個例子:

要建立提供 createlistretrieve 操作的基本檢視集類,請從 GenericViewSet 繼承,並混合(mixin )所需的操作:

from rest_framework import mixins

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
複製程式碼

通過建立自己的基本 ViewSet 類,能夠提供可在 API 中的多個檢視集中重用的常見行為。

相關文章