Entendeno Serializers no Django: A Ponte Entre Seus Modelos e a API Moderna Parte 6
- Published on
- Published on
- /5 mins read/---
Tutorial 6: ViewSets e Routers
O REST framework inclui uma abstração para lidar com ViewSets, que permite ao desenvolvedor se concentrar na modelagem do estado e das interações da API, e deixar a construção da URL para ser tratada automaticamente, com base em convenções comuns.
Classes ViewSet
são quase a mesma coisa que classes View
, exceto que elas fornecem operações como retrieve
ou update
, e não manipuladores de método como get
ou put
.
Uma classe ViewSet
só é vinculada a um conjunto de manipuladores de método no último momento, quando é instanciada em um conjunto de views, normalmente usando uma classe Router
que lida com as complexidades de definir a configuração de URL para você.
Refatorando para usar ViewSets
Vamos pegar nosso conjunto atual de views e refatorá-los em viewsets.
Primeiro, vamos refatorar nossas classes UserList
e UserDetail
em uma única classe UserViewSet
. No arquivo snippets/views.py
, podemos remover as duas classes de view e substituí-las por uma única classe ViewSet
:
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
Este viewset fornece automaticamente as ações `list` e `retrieve`.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
Aqui, usamos a classe ReadOnlyModelViewSet
para fornecer automaticamente as operações padrão 'somente leitura'. Ainda estamos definindo os atributos queryset
e serializer_class
exatamente como fazíamos quando estávamos usando views regulares, mas não precisamos mais fornecer as mesmas informações para duas classes separadas.
Em seguida, vamos substituir as classes de view SnippetList
, SnippetDetail
e SnippetHighlight
. Podemos remover as três views e, novamente, substituí-las por uma única classe.
from rest_framework import permissions
from rest_framework import renderers
from rest_framework.decorators import action
from rest_framework.response import Response
from snippets.permissions import IsOwnerOrReadOnly # Importe a permissão personalizada
class SnippetViewSet(viewsets.ModelViewSet):
"""
Este ViewSet fornece automaticamente as ações `list`, `create`, `retrieve`,
`update` e `destroy`.
Além disso, também fornecemos uma ação extra `highlight`.
"""
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)
Desta vez, usamos a classe ModelViewSet
para obter o conjunto completo de operações padrão de leitura e escrita.
Observe que também usamos o decorator @action
para criar uma ação personalizada, chamada highlight
. Este decorator pode ser usado para adicionar quaisquer endpoints personalizados que não se encaixem no estilo padrão create/update/delete
.
Ações personalizadas que usam o decorator @action
responderão a requisições GET
por padrão. Podemos usar o argumento methods
se quisermos uma ação que responda a requisições POST
.
As URLs para ações personalizadas, por padrão, dependem do próprio nome do método. Se você quiser alterar a forma como a URL deve ser construída, você pode incluir url_path
como um argumento nomeado do decorator.
Vinculando ViewSets a URLs explicitamente
Os métodos manipuladores só são vinculados às ações quando definimos o URLConf. Para ver o que está acontecendo nos bastidores, vamos primeiro criar explicitamente um conjunto de views a partir de nossos ViewSets.
No arquivo snippets/urls.py
, vinculamos nossas classes ViewSet
a um conjunto de views concretas.
from rest_framework import renderers
from snippets.views import SnippetViewSet, UserViewSet, api_root # Importe api_root
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'
})
Observe como estamos criando múltiplas views a partir de cada classe ViewSet
, vinculando os métodos HTTP à ação necessária para cada view.
Agora que vinculamos nossos recursos a views concretas, podemos registrar as views com a configuração de URL como de costume.
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')
])
Usando Routers
Como estamos usando classes ViewSet
em vez de classes View
, na verdade não precisamos projetar a configuração de URL nós mesmos. As convenções para conectar recursos a views e URLs podem ser tratadas automaticamente, usando uma classe Router
. Tudo o que precisamos fazer é registrar os viewsets apropriados com um router e deixar que ele faça o resto.
Aqui está nosso arquivo snippets/urls.py
reconectado.
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views
# Cria um router e registra nossos ViewSets com ele.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet, basename='snippet')
router.register(r'users', views.UserViewSet, basename='user')
# As URLs da API agora são determinadas automaticamente pelo router.
urlpatterns = [
path('', include(router.urls)),
]
Registrar os ViewSets com o router é semelhante a fornecer um urlpattern. Incluímos dois argumentos - o prefixo da URL para as views e o próprio viewset. O argumento basename
é usado para nomear os padrões de URL criados.
A classe DefaultRouter
que estamos usando também cria automaticamente a view raiz da API para nós, então agora podemos deletar a função api_root
do nosso módulo views
.
Compensações entre views vs ViewSets
Usar ViewSets pode ser uma abstração realmente útil. Ele ajuda a garantir que as convenções de URL sejam consistentes em toda a sua API, minimiza a quantidade de código que você precisa escrever e permite que você se concentre nas interações e representações que sua API fornece, em vez dos detalhes da configuração de URL.
Isso não significa que seja sempre a abordagem correta a ser adotada. Há um conjunto semelhante de compensações a serem consideradas como quando se usa views baseadas em classe em vez de views baseadas em função. Usar ViewSets é menos explícito do que construir suas views de API individualmente.