ViewSet

Django REST framework๋Š” ViewSet ์ด๋ผ๋Š” ๋‹จ์ผ ํด๋ž˜์Šค์—์„œ ๊ด€๋ จ๋œ ๋…ผ๋ฆฌ๋ฅผ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค. ViewSet์€ ๋‹จ์ˆœํžˆ get()์ด๋‚˜ post()์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋Œ€์‹  list() ์™€ create() ๊ฐ™์€ ์•ก์…˜์„ ์ œ๊ณตํ•œ๋‹ค(CBV ์œ ํ˜•). ViewSet ์˜ method ํ•ธ๋“ค๋Ÿฌ๋Š” as_view() ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์–ด view๊ฐ€ ๋๋‚˜๋Š” ์‹œ์ ์˜ ํ•ด๋‹น ์•ก์…˜์—๋งŒ ๋ฐ”์ธ๋”ฉ๋œ๋‹ค.

๋ฐ”์ธ๋”ฉ(binding)

๊ฐ์ข… ๊ฐ’๋“ค์ด ํ™•์ •๋˜์–ด ๋” ์ด์ƒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ์ƒํƒœ๊ฐ€ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์‹๋ณ„์ž(identifier)๊ฐ€ ๊ทธ ๋Œ€์ƒ์ธ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ, ๋ฐ์ดํ„ฐํ˜• ๋˜๋Š” ์‹ค์ œ๊ฐ’์œผ๋กœ ๋ฐฐ์ •๋˜๋Š” ๊ฒƒ์„ ๋œปํ•œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ url์„ค์ •์‹œ์— view๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋Œ€์‹  router ํด๋ž˜์Šค๋กœ ๋“ฑ๋กํ•˜์—ฌ ์ž๋™์œผ๋กœ url์„ ์„ค์ •ํ•œ๋‹ค.

START VIEWSETS

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from .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)

ํ•„์š”์‹œ์—๋Š” ์ด ViewSet์„ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐœ๋ณ„ ๋ทฐ ๋ฐ”์ธ๋”ฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})

์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐœ๋ณ„ ๋ทฐ ๋ฐ”์ธ๋”ฉ์„ ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ViewSet์„ ๋ผ์šฐํ„ฐ์— ๋“ฑ๋กํ•˜์—ฌ ์ž๋™์œผ๋กœ URLconf(url ์„ค์ •ํŒŒ์ผ)์ด ์ƒ์„ฑ๋˜๋„๋ก ํ•œ๋‹ค.

from .views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns - router.urls

์ด๋•Œ viewset์„ ๋ณธ์ธ์ด ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๊ธฐ๋ณธ ๋™์ž‘์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

class UserViewSet(viewsets.ModelViewSet):
  serializer_class = UserSerializer
  queryset = User.objects.all()

์žฅ์ 

view ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค viewsets ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์€ ์ ์ด 2๊ฐ€์ง€ ์žˆ๋‹ค.

  1. ๋ฐ˜๋ณต๋˜๋Š” ๋กœ์ง์„ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋กœ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, viewsets์„ ์ด์šฉํ•˜๋ฉด queryset์„ ๋‹จ ํ•œ๋ฒˆ๋งŒ ์ •์˜ํ•˜๋ฉด ๋œ๋‹ค.

  2. router๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, URLconf(url ์„ค์ •ํŒŒ์ผ)์„ ๋‹ค๋ฃฐ ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

ํ•˜์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์€ trade-off๋„ ์žˆ๋‹ค.

  • view์™€ url์„ค์ •์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด๋‹ค ๋ช…ํ™•ํ•˜๊ณ  ์ƒ์„ธํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

ViewSet ์€ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ๋Œ€๊ทœ๋ชจ API๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋˜๋Š” ์ „์ฒด์ ์œผ๋กœ ์ผ๊ด€๋œ URLconf๋ฅผ ์ ์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์— ์œ ๋ฆฌํ•˜๋‹ค.

MARKING EXTRA ACTIONS FOR ROUTING

REST framework์— ํฌํ•จ๋œ ๊ธฐ๋ณธ router๋Š” create / retrieve / update / destroy ์Šคํƒ€์ผ ์ž‘์—…์˜ ๊ธฐ๋ณธ method๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

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

๋ผ์šฐํŒ…์ด ํ•„์š”ํ•œ ์ž„์‹œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” @detail_router ๋˜๋Š” @list_router ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŒ…์„ ํ•„์š”๋กœ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

  • @detail_router ๋Š” URL ํŒจํ„ด์— pk๋ฅผ ํฌํ•จํ•˜๋ฉฐ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋ฅผ ์š”๊ตฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ๊ฒƒ์ด๋‹ค.

  • @list_router ๋Š” ๊ฐ์ฒด ๋ชฉ๋ก์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ๊ฒƒ์ด๋‹ค.

rom 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 .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)

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์˜ค์ง ๋ผ์šฐํŒ…๋œ View์—์„œ๋งŒ ์ถ”๊ฐ€์ ์ธ arguments๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

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

ํ•ด๋‹น ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ GET request๋ฅผ routingํ•˜์ง€๋งŒ methods ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ HTTP Method๋กœ routingํ•  ์ˆ˜ ์žˆ๋‹ค.

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

์œ„์˜ ๋‘๊ฐ€์ง€ ์ƒˆ๋กœ์šด action๋“ค์€ ^users/{pk}/set_password/$, ^users/{pk}/unset_password/$ ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

API REFERENCE

ViewSet

ViewSet ํด๋ž˜์Šค๋Š” APIView ์—์„œ ์ƒ์†๋ฐ›๋Š”๋‹ค. permission_classes, authentication_classes ์™€ ๊ฐ™์€ ํ‘œ์ค€ ์†์„ฑ์„ ์ด์šฉํ•˜์—ฌ API ์ •์ฑ…์„ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

ViewSet ํด๋ž˜์Šค๋Š” action์˜ ๊ตฌํ˜„์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ViewSet ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ overriceํ•˜๊ณ , ๋ช…์‹œ์ ์œผ๋กœ action์„ ์ •์˜ํ•ด์•ผํ•œ๋‹ค.

GenericViewSet

GenericViewSet ํด๋ž˜์Šค๋Š” GenericAPIView ์—์„œ ์ƒ์† ๋ฐ›๋Š”๋‹ค. get_object, get_queryset ๋ฉ”์†Œ๋“œ์™€ ๋‹ค๋ฅธ generic view์˜ ๊ธฐ๋ณธ action์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ์–ด๋– ํ•œ ์•ก์…˜๋„ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

GenericViewSet ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ overrideํ•˜๊ณ  ํ•„์š”ํ•œ mixin ํด๋ž˜์Šค๋ฅผ ํ˜ผํ•ฉํ•˜์—ฌ ๋ช…์‹œ์ ์œผ๋กœ action์„ ์ •์˜ํ•ด์•„ํ•œ๋‹ค.

ModelViewSet

ModelViewSet ํด๋ž˜์Šค๋Š” GenericAPIView ๋ฅผ ์ƒ์†ํ•˜๊ณ  ๋‹ค์ˆ˜์˜ mixin ํด๋ž˜์Šค์˜ ๋™์ž‘์„ ํ˜ผํ•ฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ action์— ๋Œ€ํ•œ ๊ตฌํ˜„์„ ํฌํ•จํ•œ๋‹ค.

.list(), .retrieve(), .create(), .update(), .partial_update(), .destroy() ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

GenericAPIView ๋ฅผ ์ƒ์†๋ฐ›๊ณ  ์žˆ๊ธฐ ๋Œ€๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ queryset๊ณผ serializer_class ์†์„ฑ์„ ํ•„์ˆ˜๋กœ ์ œ๊ณตํ•ด์•ผํ•œ๋‹ค.

class AccountViewSet(viewsets.ModelViewSet):
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

    def get_queryset(self):
        return self.request.user.accounts.all()

queryset์„ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๋Š” viewsets์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์œ„์™€ ๊ฐ™์ด ๋ฉ”์†Œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•ด์•ผํ•  ์ ์ด ์žˆ๋‹ค. ViewSet์˜ queryset ์†์„ฑ์„ ์ œ๊ฑฐํ•˜๋ฉด ์—ฐ๊ฒฐ๋œ ๋ผ์šฐํ„ฐ๊ฐ€ ๋ชจ๋ธ์˜ base_name์„ ์ž๋™์œผ๋กœ ํŒŒ์ƒ์‹œํ‚ฌ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ base_name์˜ kwarg๋ฅผ ์ง€์ •ํ•ด์•ผํ•œ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ create / list / retrieve / update /destroy ์•ก์…˜์„ ์ œ๊ณตํ•˜์ง€๋งŒ ํ‘œ์ค€ ๊ถŒํ•œ ํด๋ž˜์Šค(permission)๋ฅผ ์‚ฌ์šฉํ•ด ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

ReadOnlyModelViewSet

ReadOnlyModelViewSet ํด๋ž˜์Šค๋Š” GenericAPIView ๋ฅผ ์ƒ์†ํ•œ๋‹ค. ModelViewSet๊ณผ ๋™์ผํ•˜๊ฒŒ ๋‹ค์–‘ํ•œ action์•  ๋Œ€ํ•œ ๊ตฌํ˜„๋„ ํฌํ•จ๋˜์ง€๋งŒ ์˜ค์ง ์ฝ๊ธฐ ์ „์šฉ action์ธ .list() ์™€ .retrieve()๋งŒ ์ œ๊ณตํ•œ๋‹ค.

class AccountViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

์ฐธ๊ณ ๋งํฌ

Last updated