튜토리얼6: ViewSets & Routers

REST 프레임워크에는 개발자가 API의 상태 및 상호 작용을 모델링하는 데 집중하고 일반적인 규칙에 따라 URL 구성을 자동으로 처리 할 수 있도록 해주는 ViewSets을 다루는 추상화가 포함되어 있습니다.

ViewSet 클래스는 read 또는 update와 같은 작업을 제공하고 get 또는 put과 같은 메소드 처리기는 제공하지 않는다는 점을 제외하고는 View 클래스와 거의 동일합니다.

ViewSet 클래스는 마지막 순간에 뷰 집합으로 인스턴스화 될 때, 일반적으로 URL conf를 정의하는 복잡성을 처리하는 Router 클래스를 사용하여 메소드 핸들러 세트에만 바인딩됩니다.

ViewSet을 사용하기위한 리팩토링

현재의 뷰 집합을 가져 와서 뷰 집합으로 리팩터링합니다.

먼저 UserListUserDetail 뷰를 단일 UserViewSet으로 리팩토링합시다. 우리는 두개의 뷰를 제거하고 그것들을 하나의 클래스로 대체 할 수 있습니다.

from rest_framework import viewsets

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    This viewset automatically provides `list` and `detail` actions.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

여기에서는 ReadOnlyModelViewSet 클래스를 사용하여 자동으로 기본 ‘읽기 전용’작업을 제공합니다. 우리는 여전히 일반 뷰를 사용할 때와 마찬가지로 querysetserializer_class 특성을 정확하게 설정하지만 더 이상 두 개의 별도 클래스에 동일한 정보를 제공 할 필요가 없습니다.

다음으로 SnippetList, SnippetDetailSnippetHighlight뷰 클래스를 대체 할 것입니다. 세가지 뷰를 제거하고 다시 단일 클래스로 대체 할 수 있습니다.

from rest_framework.decorators import action
from rest_framework.response import Response

class SnippetViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.

    Additionally we also provide an extra `highlight` action.
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)

    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

이번에는 ModelViewSet 클래스를 사용하여 완전한 기본 읽기 및 쓰기 작업 세트를 가져 왔습니다.

@action 데코레이터를 사용하여 highlight라는 사용자 정의 액션을 만들었습니다. 이 데코레이터는 표준 create/update/delete 스타일에 맞지 않는 사용자 정의 엔드포인트를 추가하는데 사용될 수 있습니다.

@action 데코레이터를 사용하는 사용자 정의 액션은 기본적으로 GET 요청에 응답합니다. POST 요청에 응답한 작업을 원한다면 methods 인수를 사용할 수 있습니다.

사용자 지정 작업의 URL은 기본적으로 메서드 이름 자체에 따라 다릅니다. url을 구성하는 방법을 변경하려면 url_path를 데코레이터 키워드 인수로 포함 할 수 있습니다.

Binding ViewSets to URLs explicitly

핸들러 메서드는 URLConf를 정의 할 때만 액션에 바인딩됩니다. 내부에서 진행중인 작업을 보려면 먼저 ViewSet에서 명시 적으로 일련의보기를 만듭니다.

snippets/urls.py 파일에서 우리는 ViewSet 클래스를 구체적인 뷰 세트에 바인딩합니다.

from snippets.views import SnippetViewSet, UserViewSet, api_root
from rest_framework import renderers

snippet_list = SnippetViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
    'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])
user_list = UserViewSet.as_view({
    'get': 'list'
})
user_detail = UserViewSet.as_view({
    'get': 'retrieve'
})

http 메소드를 각 뷰에 대해 필요한 액션에 바인딩하여 각 ViewSet 클래스에서 여러 뷰를 생성하는 방법에 주목하십시오.

이제 리소스를 concrete view로 묶었으므로 평소처럼 URL conf를 사용하여 뷰를 등록 할 수 있습니다.

urlpatterns = format_suffix_patterns([
    url(r'^$', api_root),
    url(r'^snippets/$', snippet_list, name='snippet-list'),
    url(r'^snippets/(?P<pk>[0-9]+)/$', snippet_detail, name='snippet-detail'),
    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),
    url(r'^users/$', user_list, name='user-list'),
    url(r'^users/(?P<pk>[0-9]+)/$', user_detail, name='user-detail')
])

Using Routers

View 클래스보다는 ViewSet 클래스를 사용하기 때문에 실제로 URL을 직접 디자인 할 필요가 없습니다. 보기 및 URL에 자원을 연결하는 규칙은 Router 클래스를 사용하여 자동으로 처리 할 수 있습니다. 적절한 뷰 세트를 라우터에 등록하고 나머지는 그대로두면됩니다.

from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from snippets import views

# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)

# The API URLs are now determined automatically by the router.
urlpatterns = [
    url(r'^', include(router.urls))
]

뷰 세트를 라우터에 등록하는 것은 url 패턴을 제공하는 것과 유사합니다. 뷰의 URL 접두어와 뷰 세트 자체라는 두 가지 인수가 포함됩니다.

우리가 사용하고있는 DefaultRouter 클래스는 자동으로 API 루트 뷰를 생성하기 때문에 view 모듈에서 api_root 메소드를 삭제할 수 있습니다.

views와 viewsets의 절충(Trade-offs)

뷰 세트를 사용하면 정말 유용한 추상화가 될 수 있습니다. API를 통해 URL 규칙이 일관되게 유지되고 작성해야하는 코드의 양을 최소화하며 URL conf의 특성보다는 API가 제공하는 상호 작용 및 표현에 집중할 수 있습니다.

그렇다고 항상 올바른 접근 방식을 취하는 것은 아닙니다. 함수 기반 뷰 대신 클래스 기반 뷰를 사용할 때와 마찬가지로 고려해야 할 유사한 절충점이 있습니다. viewsets를 사용하는 것은 뷰를 개별적으로 작성하는 것보다 덜 명백합니다.