viewsets.py

ビューセット

ルーティングがリクエストに使用するコントローラを決定した後、コントローラはリクエストを理解し、適切な出力を生成する責任があります。

Ruby on Railsドキュメント

Django REST frameworkでは、関連するビューのセットのロジックを、ViewSetと呼ばれる単一のクラスに組み合わせることができます。他のフレームワークでは、「リソース」または「コントローラー」のような概念的に類似した実装が見つかる場合があります。

ViewSetクラスは、単に**` .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):
    """
    A simple ViewSet for listing or retrieving users.
    """
    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)

必要に応じて、このビューセットを次のように2つの別々のビューにバインドできます。

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, basename='user')
urlpatterns = router.urls

独自のビューセットを作成するのではなく、デフォルトの動作セットを提供する既存の基底クラスを使用することがよくあります。例えば

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

ViewクラスではなくViewSetクラスを使用することには、2つの主な利点があります。

  • 繰り返されるロジックを単一のクラスに組み合わせることができます。上記の例では、` queryset `を一度だけ指定するだけで、複数のビューで使用されます。
  • ルータを使用することにより、URLconfを自分で配線する必要がなくなります。

これらの両方にはトレードオフが伴います。通常のビューとURLconfを使用する方が明示的で、より多くの制御を行うことができます。ViewSetは、すばやく起動して実行したい場合、または大規模なAPIがあり、URL構成全体で一貫性を強制したい場合に役立ちます。

ビューセットアクション

REST frameworkに含まれるデフォルトのルータは、以下に示すように、標準的な作成/取得/更新/削除スタイルのアクションのルートを提供します。

class UserViewSet(viewsets.ViewSet):
    """
    Example empty viewset demonstrating the standard
    actions that will be handled by a router class.

    If you're using format suffixes, make sure to also include
    the `format=None` keyword argument for each action.
    """

    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

ビューセットアクションのイントロスペクション

ディスパッチ中に、ViewSetで次の属性を使用できます。

  • basename - 作成されるURL名に使用するベース。
  • action - 現在のアクションの名前(例:` list `、` create `)。
  • detail - 現在のアクションがリストビューまたは詳細ビュー用に構成されているかどうかを示すブール値。
  • suffix - ビューセットタイプの表示サフィックス。 `detail`属性を反映します。
  • name - ビューセットの表示名。この引数は` suffix `と相互に排他的です。
  • description - ビューセットの個々のビューの表示説明。

現在のアクションに基づいて動作を調整するために、これらの属性を検査できます。たとえば、次のように、` list `アクション以外のすべてにパーミッションを制限できます。

def get_permissions(self):
    """
    Instantiates and returns the list of permissions that this view requires.
    """
    if self.action == 'list':
        permission_classes = [IsAuthenticated]
    else:
        permission_classes = [IsAdminUser]
    return [permission() for permission in permission_classes]

ルーティングのための追加アクションのマーキング

ルーティング可能なアドホックメソッドがある場合は、` @action `デコレータを使用して、そのようにマークできます。 通常のアクションと同様に、追加のアクションは、単一のオブジェクトまたはコレクション全体を対象とする場合があります。これを示すには、` detail `引数を` True `または` False `に設定します。ルータは、それに応じてURLパターンを構成します。たとえば、` DefaultRouter `は、URLパターンに` pk `を含めるように詳細アクションを構成します。

追加アクションのより完全な例

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

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, 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.validated_data['password'])
            user.save()
            return Response({'status': 'password set'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False)
    def recent_users(self, request):
        recent_users = User.objects.all().order_by('-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)

`action`デコレータはデフォルトで`GET`リクエストをルーティングしますが、`methods`引数を設定することで他のHTTPメソッドも受け入れる場合があります。例えば

    @action(detail=True, methods=['post', 'delete'])
    def unset_password(self, request, pk=None):
       ...

引数` methods `は、HTTPMethodとして定義されたHTTPメソッドもサポートしています。以下の例は上記と同じです

    from http import HTTPMethod

    @action(detail=True, methods=[HTTPMethod.POST, HTTPMethod.DELETE])
    def unset_password(self, request, pk=None):
       ...

デコレータを使用すると、` permission_classes `、` serializer_class `、` filter_backends `などのビューセットレベルの構成をオーバーライドできます...

    @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
       ...

2つの新しいアクションは、URL `^ users / {pk} / set_password / $`および`^ users / {pk} / unset_password / $`で使用できるようになります。 `url_path`および`url_name`パラメータを使用して、アクションのURLセグメントとリバースURL名を変更します。

すべての追加のアクションを表示するには、`.get_extra_actions()`メソッドを呼び出します。

追加アクションの追加HTTPメソッドのルーティング

追加のアクションは、追加のHTTPメソッドを個別の`ViewSet`メソッドにマッピングできます。たとえば、上記のパスワード設定/設定解除メソッドを単一のルートに統合できます。追加のマッピングは引数を受け入れないことに注意してください。

@action(detail=True, methods=["put"], name="Change Password")
def password(self, request, pk=None):
    """Update the user's password."""
    ...


@password.mapping.delete
def delete_password(self, request, pk=None):
    """Delete the user's password."""
    ...

アクションURLのリバース

アクションのURLを取得する必要がある場合は、`.reverse_action()`メソッドを使用します。これは`reverse()`のコンビニエンスラッパーであり、ビューの`request`オブジェクトを自動的に渡し、`url_name`の前に`.basename`属性を付加します。

`basename`は、`ViewSet`の登録中にルータによって提供されることに注意してください。ルータを使用していない場合は、`.as_view()`メソッドに`basename`引数を指定する必要があります。

前のセクションの例を使用する

>>> view.reverse_action("set-password", args=["1"])
'http://localhost:8000/api/users/1/set_password'

あるいは、` @action `デコレータによって設定された` url_name `属性を使用することもできます。

>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'

`.reverse_action()`の` url_name `引数は、` @action `デコレータの同じ引数と一致する必要があります。さらに、このメソッドを使用して、` list `や` create `などのデフォルトのアクションを反転できます。


APIリファレンス

ViewSet

`ViewSet`クラスは`APIView`から継承します。 `permission_classes`、`authentication_classes`などの標準属性を使用して、ビューセットのAPIポリシーを制御できます。

`ViewSet`クラスは、アクションの実装を提供しません。 `ViewSet`クラスを使用するには、クラスをオーバーライドし、アクションの実装を明示的に定義します。

GenericViewSet

`GenericViewSet`クラスは`GenericAPIView`から継承し、デフォルトの`get_object`、`get_queryset`メソッドのセットとその他の汎用ビューの基本動作を提供しますが、デフォルトではアクションを含みません。

`GenericViewSet`クラスを使用するには、クラスをオーバーライドし、必要なmixinクラスをmixinするか、アクションの実装を明示的に定義します。

ModelViewSet

`ModelViewSet`クラスは`GenericAPIView`から継承し、さまざまなmixinクラスの動作をmixinすることにより、さまざまなアクションの実装を含みます。

`ModelViewSet`クラスによって提供されるアクションは、` .list() `、` .retrieve() `、` .create() `、` .update() `、` .partial_update() `、および` .destroy()`です。

`ModelViewSet`は`GenericAPIView`を拡張するため、通常は少なくとも`queryset`属性と`serializer_class`属性を提供する必要があります。例えば

class AccountViewSet(viewsets.ModelViewSet):
    """
    A simple ViewSet for viewing and editing accounts.
    """
    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`プロパティを削除すると、関連付けられているルータはモデルのベース名を自動的に派生できなくなるため、ルータ登録の一部として`basename`キーワード引数を指定する必要があることに注意してください。

また、このクラスはデフォルトで作成/リスト/取得/更新/破棄アクションの完全なセットを提供しますが、標準のパーミッションクラスを使用して、使用可能な操作を制限できることに注意してください。

ReadOnlyModelViewSet

`ReadOnlyModelViewSet`クラスも`GenericAPIView`から継承します。 `ModelViewSet`と同様に、さまざまなアクションの実装も含まれていますが、`ModelViewSet`とは異なり、「読み取り専用」アクションである` .list() `と` .retrieve() `のみを提供します。

`ModelViewSet`と同様に、通常は少なくとも` queryset `属性と` serializer_class `属性を提供する必要があります。例えば

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

繰り返しますが、` ModelViewSet `と同様に、` GenericAPIView `で使用可能な標準属性とメソッドオーバーライドのいずれかを使用できます。

カスタムビューセット基底クラス

`ModelViewSet`アクションの完全なセットを持たない、または何らかの方法で動作をカスタマイズするカスタム`ViewSet`クラスを提供する必要がある場合があります。

createlist、およびretrieve操作を提供する基本ビューセットクラスを作成するには、GenericViewSetを継承し、必要なアクションをmixinします。

from rest_framework import mixins, viewsets

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全体で複数のビューセットで再利用できる共通の動作を提供できます。