Урок 6: ViewSets и маршрутизаторы
REST фреймворк включает абстракцию для работы с ViewSets, что позволяет разработчику сосредоточиться на моделировании состояния и взаимодействий API, а построение URL-адресов оставить на автоматическое управление, основанное на общих соглашениях.
ViewSet классы почти идентичны View классам, за исключением того, что они предоставляют операции, такие как retrieve, или update, а не обработчики методов, такие как get или put.
Класс ViewSet связывается с набором обработчиков методов только в последний момент, когда он преобразуется в набор представлений, обычно с помощью класса Router , который обрабатывает сложность определения URL-конфигурации за вас.
Рефакторинг для использования ViewSets
Давайте возьмём наш текущий набор представлений и перепишем их в виде наборов представлений.
Прежде всего, давайте перепишем наши UserList и UserDetail классы в один класс UserViewSet. В файле snippets/views.py мы можем удалить два класса представления и заменить их одним классом 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 так же, как и при использовании обычных представлений, но больше не нужно предоставлять ту же информацию двум отдельным классам.
Далее мы собираемся заменить классы представлений SnippetList, SnippetDetail и SnippetHighlight. Мы можем удалить три представления и снова заменить их одним классом.
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. Мы можем использовать аргумент methods, если нам нужно действие, которое отвечает на запросы POST.
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-конфигурации как обычно.
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')
])
Использование маршрутизаторов
Поскольку мы используем классы ViewSet вместо классов View, нам на самом деле не нужно самостоятельно проектировать URL-конфигурацию. Соглашения по подключению ресурсов к представлениям и URL-адресам можно обрабатывать автоматически с помощью класса Router . Всё, что нам нужно сделать, это зарегистрировать соответствующие наборы представлений с маршрутизатором и позволить ему сделать остальное.
Вот наш переработанный файл 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)),
]
Регистрация ViewSets с маршрутизатором аналогична предоставлению urlpattern. Мы включаем два аргумента — префикс URL-адреса для представлений и сам набор представлений.
Класс DefaultRouter , который мы используем, также автоматически создаёт для нас корневое представление API, поэтому мы можем удалить функцию api_root из нашего модуля views.
Сравнительный анализ использования представлений и ViewSets
Использование ViewSets может быть очень полезной абстракцией. Оно помогает обеспечить согласованность URL-конвенций в вашем API, минимизирует количество необходимого кода и позволяет сосредоточиться на взаимодействиях и представлениях вашего API, а не на деталях URL-конфигурации.
Это не означает, что это всегда правильный подход. Существует аналогичный набор компромиссов, которые следует учитывать, как и при использовании представлений на основе классов вместо представлений на основе функций. Использование ViewSets менее явное, чем создание представлений API по отдельности.
Copyright © 2011–present Encode OSS Ltd.
Licensed under the BSD License.
https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/