DRF - ViewSet


ViewSet

  • は複数のAPIの機能を統合し、1つのAPIセットとして
  • を提供する.
  • のモデルを使用してlist、detailなどの各APIを作成すると、重複する論理がたくさん見つかります.この場合、ViewSetを使用すると、重複する論理のコードを減らすことができ、コードの効率を向上させることができる.
  • ViewSetsは、getpostなどの方法ハンドルを提供しないが、listcreateなどの動作を提供する.
  • ViewSetのメソッドハンドルは、as view()関数を使用してビューの終了時に対応する動作でバインドされます.
  • は、url設定内でviewsetビューを明示的に登録するよりも、routerクラスを使用してviewsetを登録するのが一般的です.
  • Code


    修正テストは、前のDRFのAuth部分コードに続いて行われる.

    ViewSet


    profiles.api.views.py
    変更前
    from rest_framework import generics
    from rest_framework.permissions import IsAuthenticated
    
    from profiles.models import Profile
    from profiles.api.serializers import ProfileSerializer
    
    
    class ProfileList(generics.ListAPIView):
        queryset = Profile.objects.all()
        serializer_class = ProfileSerializer
        permission_classes = [IsAuthenticated]
    変更後
    from rest_framework.viewsets import ReadOnlyModelViewSet
    
    class ProfileViewSet(ReadOnlyModelViewSet):
        queryset = Profile.objects.all()
        serializer_class = ProfileSerializer
        permission_classes = [IsAuthenticated]
    profiles.api.urls.py
    変更前
    from django.urls import path
    from profiles.api.views import ProfileList
    
    urlpatterns = [
        path("profiles/", ProfileList.as_view(), name="profile-list")
    ]
    変更後
  • viewsetのパスを1つずつ指定しました.
  • from django.urls import path
    from profiles.api.views import ProfileViewSet
    
    profile_list = ProfileViewSet.as_view({"get": "list"})
    profile_detail = ProfileViewSet.as_view({"get": "retrieve"})
    
    urlpatterns = [
        path("profiles/", profile_list, name="profile-list"),
        path("profiles/<int:pk>/", profile_detail, name="profile-detail")
    ]

    ViewSet + Router


    profiles.api.urls.py
    routerを使用すると、パス指定はDRFによって自動的に実行され、通常viewsetのurlはrouterに設定されます.
    from django.urls import include, path
    from rest_framework.routers import DefaultRouter
    from profiles.api.views import ProfileViewSet
    
    # router는 자동으로 link들을 생성해낸다.
    router = DefaultRouter()
    router.register(r"profiles", ProfileViewSet)
    
    urlpatterns = [
        path("", include(router.urls))
    ]
    テスト結果
  • list
  • get
  • root
  • router.register() method

    router.register(r"status", ProfileStatusViewSet, basename="status")
    必須パラメータ
  • prefix:urlのリソース名(ルーティングセットのURL接頭辞を使用)
  • viewset : viewset class
  • オプションのパラメータ
  • basename:djangoで使用されているpath nameのようにビューに名前を付けます
  • の空のボックスを保持すると、viewsetを参照して自動的に生成されます.したがって、viewsetでquerySetを指定する必要があります.
  • viewsetにquerysetプロパティが含まれていない場合、viewsetを登録するときにbase nameを指定する必要があります.
  • ViewSet+Update機能の追加


    profiles.api.views.py
    from rest_framework import viewsets
    from rest_framework import mixins
    from profiles.api.permissions import IsOwnProfileOrReadOnly
    
    class ProfileViewSet(mixins.UpdateModelMixin,
                         mixins.ListModelMixin,
                         mixins.RetrieveModelMixin,
                         viewsets.GenericViewSet):
    
        queryset = Profile.objects.all()
        serializer_class = ProfileSerializer
        permission_classes = [IsAuthenticated, IsOwnProfileOrReadOnly]
    profiles.api.permissions.py
    from rest_framework import permissions
    
    class IsOwnProfileOrReadOnly(permissions.BasePermission):
    
        def has_object_permission(self, request, view, obj):
            if request.method in permissions.SAFE_METHODS:
                return True
    
            return obj.user == request.user
    結果
  • 承認時(所有者)
  • 無許可の場合
  • ViewSet+update&delete機能の追加


    profiles.api.views.py
    from rest_framework.viewsets import ModelViewSet
    from profiles.models import ProfileStatus
    from profiles.api.serializers import ProfileStatusSerializer
    from profiles.api.permissions import IsOwnerOrReadOnly
    
    class ProfileStatusViewSet(ModelViewSet):
        queryset = ProfileStatus.objects.all()
        serializer_class = ProfileStatusSerializer
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    
        def perform_create(self, serializer):
            user_profile = self.request.user.profile
            serializer.save(user_profile=user_profile)
    
    profiles.api.permissions.py
    class IsOwnerOrReadOnly(permissions.BasePermission):
    
        def has_object_permission(self, request, view, obj):
            if request.method in permissions.SAFE_METHODS:
                return True
    
            return obj.user_profile == request.user.profile
    profiles.api.urls.py
    from profiles.api.views import ProfileViewSet, ProfileStatusViewSet
    
    # 추가
    router.register(r"status", ProfileStatusViewSet)
    結果
  • Api Root
  • api/status/
  • post
  • api/status/
  • api/status/1/
  • AvatarUpdateViewの追加


    profiles.api.views.py
    from profiles.api.serializers import ProfileAvatarSerializer
    
    class AvatarUpdateView(generics.UpdateAPIView):
        serializer_class = ProfileAvatarSerializer
        permission_classes = [IsAuthenticated]
    
        def get_object(self):
            profile_object = self.request.user.profile
            return profile_object
    profiles.api.urls.py
    from profiles.api.views import ..., AvatarUpdateView
    
    urlpatterns = [
        ...,
        path("avatar/", AvatarUpdateView.as_view(), name="avatar-update")
    ]
    結果

    良いウェブサイト
  • [Django]ViewSetとRouter
  • DRF(Django Rest Framework)ビューとGeneric ViewとViewはいったい何を書けばいいのでしょうか。