チュートリアル6:ViewSetsとルーター
REST frameworkは、開発者がAPIの状態と相互作用のモデリングに集中し、一般的な規約に基づいてURL構築を自動的に処理できるようにする、ViewSets
を扱うための抽象化を提供します。
ViewSet
クラスはView
クラスとほぼ同じですが、get
やput
などのメソッドハンドラーではなく、retrieve
やupdate
などの操作を提供します。
ViewSet
クラスは、通常はURL confの複雑さを処理するRouter
クラスを使用して、一連のビューにインスタンス化される最後の瞬間にのみ、一連のメソッドハンドラーにバインドされます。
ViewSetsを使うためのリファクタリング
現在のビューセットをリファクタリングしてViewSetsにしましょう。
まず、UserList
とUserDetail
クラスを単一のUserViewSet
クラスにリファクタリングします。snippets/views.py
ファイルで、2つのビュークラスを削除し、単一のViewSetクラスに置き換えることができます。
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
クラスを使用しました。通常のビューを使用していたときと同様に、queryset
とserializer_class
属性を設定していますが、2つの別々のクラスに同じ情報を提供する必要がなくなりました。
次に、SnippetList
、SnippetDetail
、SnippetHighlight
ビュークラスを置き換えます。3つのビューを削除し、再び単一のクラスに置き換えることができます。
from rest_framework import permissions
from rest_framework import renderers
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
を含めることができます。
ViewSetsをURLに明示的にバインドする
ハンドラーメソッドは、URLConfを定義したときにのみアクションにバインドされます。内部で何が起こっているかを確認するために、まずViewSetsから明示的にビューセットを作成してみましょう。
snippets/urls.py
ファイルで、ViewSet
クラスを一連の具体的なビューにバインドします。
from rest_framework import renderers
from snippets.views import api_root, SnippetViewSet, UserViewSet
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'
})
各ViewSet
クラスから複数のビューを作成し、HTTPメソッドを各ビューに必要なアクションにバインドする方法に注意してください。
リソースを具体的なビューにバインドしたので、通常どおりURL confにビューを登録できます。
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')
])
ルーターを使う
View
クラスではなくViewSet
クラスを使用しているため、実際にはURL confを自分で設計する必要はありません。リソースをビューとurlに接続するための規約は、Router
クラスを使用して自動的に処理できます。必要なViewSetをルーターに登録し、残りを処理させるだけです。
リワイヤードされたsnippets/urls.py
ファイルを以下に示します。
from django.urls import path, 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, basename='snippet')
router.register(r'users', views.UserViewSet, basename='user')
# The API URLs are now determined automatically by the router.
urlpatterns = [
path('', include(router.urls)),
]
ルーターにViewSetを登録する方法は、urlpatternを提供するのと似ています。ビューのURLプレフィックスとViewSet自体という2つの引数を指定します。
使用しているDefaultRouter
クラスは、APIルートビューも自動的に作成するため、views
モジュールからapi_root
関数を削除できます。
ビューとViewSetsのトレードオフ
ViewSetsを使用することは非常に便利な抽象化です。API全体でURL規約が確実に一貫性を保ち、記述する必要があるコードの量を最小限に抑え、URL confの具体的な内容ではなく、APIが提供する相互作用と表現に集中できます。
ただし、それが常に最適なアプローチであるとは限りません。関数ベースのビューの代わりにクラスベースのビューを使用する場合と同様に、考慮すべきトレードオフがあります。ViewSetsを使用すると、APIビューを個別に構築するよりも明示性が低くなります。