Skip to content

8. ViewSets & Routers

1. Refactoring to user ViewSets

  • 뷰 세트를 리팩토링해 보자.
  • 먼저, 다음과 같이 UserListUserDetail 뷰를 하나의 UserViewSet으로 리팩토링한다.


snippets/views.py
from rest_framework import viewsets

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    This viewset automatically provides 'list' and 'retrieve' actions.
    """

    queryset = User.objects.all()
    serializer_class = UserSerializer


  • ReadOnlyModelViewSet 클래스를 사용하여 기본 '읽기 전용' 작업을 자동으로 제공한다.
  • 여전히 일반 뷰를 사용할 때와 같이 querysetserializer_class 속성을 설정하고 있지만, 더 이상 두 개의 개별 클래스에 동일한 정보를 제공할 필요가 없다.


  • 다음으로 SnippetList, SnippetDetailSnippetHighlight 뷰 클래스를 리팩토링한다.


snippets/views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import permissions

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, reqeust, *args, **kwargs):
        snippet = self.get_object()

        return Response(snippet.highlighted)

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


  • 기본 읽기 및 쓰기 작업의 전체 집합을 가져오기 위해 ModelViewSet 클래스를 사용했다.
  • highlight라는 사용자 지정 작업을 만들기 위해 @action 데코레이터도 사용했다.
  • 이 데코레이터는 표준 create/update/delete 스타일에 맞지 않는 사용자 지정 엔드포인트를 추가하는 데 사용할 수 있다.
  • @action 데코레이터를 사용하는 사용자 지정 작업은 기본적으로 GET 요청에 응답한다.
  • POST 요청에 응답하는 작업을 원하는 경우 methods 인수를 사용할 수 있다.
  • 기본적으로 사용자 지정 작업의 URL은 메서드 이름 자체에 따라 다르다.
  • url이 구성되는 방식을 변경하려면 url_path를 데코레이터 키워드 인수로 포함하면 된다.


2. Binding ViewSets to URLs explicitly

  • 핸들러 메서드는 URLConf를 정의할 때만 작업에 바인딩된다.
  • 내부에서 무슨 일이 일어나고 있는지 보기 위해 먼저 뷰 세트를 명시적으로 생성해 보자.
  • snippets/urls.py 파일에서 ViewSet 클래스를 구체적인 뷰 세트에 바인딩한다.


snippets/urls.py
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.set_view({"get": "list"})
user_detail = UserViewSet.as_view({"get": "retrieve"})


  • HTTP 메서드를 각 뷰에 필요한 작업에 바인딩하여 각 ViewSet 클래스에서 여러 뷰를 만들었다.
  • 이제 리소스를 구체적인 뷰에 바인딩했으므로 평소와 같이 URLConf를 사용하여 뷰를 등록할 수 있다.


snippets/views.py
urlpatterns = format_suffix_patterns(
    [
        path["", api_root],
        path("snippets/", snippet_list, name="snippet-list"),
        path("snippets/<int:pk>/", snippet_detail, name="snippet-detail"),
        path(
            "snippets/<int:pk>/highlight/", snippet_highlight, name="snippet-highlight"
        ),
        path("users/", user_list, name="user-list"),
        path("users/<int:pk>/", user_detail, name="user-detail"),
    ]
)


3. Using Routers

  • View 클래스가 아닌 ViewSet 클래스를 사용하기 때문에 실제로 URLConf를 직접 디자인할 필요가 없다.
  • 리소스를 뷰와 URL에 연결하는 규칙은 Router 클래스를 사용하여 자동으로 처리할 수 있다.
  • 라우터에 적절한 뷰 세트를 등록하고 나머지는 라우터가 처리하도록 하면 된다.
  • snippets/urls.py 파일을 열어 다음과 같이 작성한다.


snippets/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views

router = DefaultRouter()
router.register(r"snippets", views.SnippetViewSet)
router.register(r"users", views.UserViewSet)

urlpatterns = [
    path("", include(router.urls)),
]


  • 라우터에 뷰 세트를 등록하는 것은 urlpatterns를 제공하는 것과 유사하다.
  • 뷰에 대한 URL 접두사와 뷰 세트 자체의 두 가지 인수를 포함한다.
  • DefaultRouter 클래스도 자동으로 API 루트 뷰를 생성하므로 이제 views 모듈에서 api_root 메서드를 삭제할 수 있다.
  • 이러한 뷰 세트를 사용하는 것은 뷰를 개별적으로 작성하는 것보다 덜 명시적이기 때문에 상황에 맞춰 사용한다.

References