Entendeno Serializers no Django: A Ponte Entre Seus Modelos e a API Moderna Parte 5
- Published on
- Published on
- /5 mins read/---
Tutorial 5: Relacionamentos e APIs Hiperlinkadas
No momento, os relacionamentos dentro da nossa API são representados pelo uso de chaves primárias. Nesta parte do tutorial, vamos melhorar a coesão e a capacidade de descoberta da nossa API, usando hiperlinks para os relacionamentos.
Criando um endpoint para a raiz da nossa API
Atualmente, temos endpoints para 'snippets' e 'users', mas não temos um único ponto de entrada para a nossa API. Para criar um, usaremos uma view regular baseada em função e o decorator @api_view
que introduzimos anteriormente. No seu arquivo snippets/views.py
, adicione:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})
Duas coisas devem ser observadas aqui. Primeiro, estamos usando a função reverse
do REST framework para retornar URLs totalmente qualificadas; segundo, os padrões de URL são identificados por nomes de conveniência que declararemos mais tarde em nosso snippets/urls.py
.
Criando um endpoint para os snippets realçados
A outra coisa óbvia que ainda está faltando em nossa API pastebin são os endpoints de realce de código.
Ao contrário de todos os nossos outros endpoints da API, não queremos usar JSON, mas sim apresentar uma representação HTML. Existem dois estilos de renderizadores HTML fornecidos pelo REST framework, um para lidar com HTML renderizado usando templates e outro para lidar com HTML pré-renderizado. O segundo renderizador é o que gostaríamos de usar para este endpoint.
A outra coisa que precisamos considerar ao criar a view de realce de código é que não há uma view genérica concreta existente que possamos usar. Não estamos retornando uma instância de objeto, mas sim uma propriedade de uma instância de objeto.
Em vez de usar uma view genérica concreta, usaremos a classe base para representar instâncias e criaremos nosso próprio método .get()
. No seu snippets/views.py
, adicione:
from rest_framework import renderers
from rest_framework import generics
class SnippetHighlight(generics.GenericAPIView):
queryset = Snippet.objects.all()
renderer_classes = [renderers.StaticHTMLRenderer]
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
Como de costume, precisamos adicionar as novas views que criamos em nosso URLconf. Adicionaremos um padrão de URL para nossa nova raiz da API em snippets/urls.py
:
path('', views.api_root),
E então adicione um padrão de URL para os destaques do snippet:
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),
Hiperlinkando nossa API
Lidar com relacionamentos entre entidades é um dos aspectos mais desafiadores do design de APIs Web. Existem várias maneiras diferentes de representar um relacionamento:
- Usando chaves primárias.
- Usando hiperlinks entre entidades.
- Usando um campo slug identificador único na entidade relacionada.
- Usando a representação de string padrão da entidade relacionada.
- Aninhando a entidade relacionada dentro da representação pai.
- Alguma outra representação personalizada.
O REST framework suporta todos esses estilos e pode aplicá-los em relacionamentos diretos ou reversos, ou aplicá-los em gerenciadores personalizados, como chaves estrangeiras genéricas.
Neste caso, gostaríamos de usar um estilo hiperlinkado entre as entidades. Para isso, modificaremos nossos serializadores para estender HyperlinkedModelSerializer
em vez do ModelSerializer
existente.
O HyperlinkedModelSerializer
tem as seguintes diferenças do ModelSerializer
:
- Ele não inclui o campo
id
por padrão. - Ele inclui um campo
url
, usandoHyperlinkedIdentityField
. - Os relacionamentos usam
HyperlinkedRelatedField
, em vez dePrimaryKeyRelatedField
.
Podemos facilmente reescrever nossos serializadores existentes para usar hiperlinks. Em seu snippets/serializers.py
, adicione:
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')
class Meta:
model = Snippet
fields = ['url', 'id', 'highlight', 'owner',
'title', 'code', 'linenos', 'language', 'style']
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
class Meta:
model = User
fields = ['url', 'id', 'username', 'snippets']
Observe que também adicionamos um novo campo 'highlight'. Este campo é do mesmo tipo que o campo url
, exceto que ele aponta para o padrão de URL 'snippet-highlight', em vez do padrão de URL 'snippet-detail'.
Como incluímos URLs com sufixo de formato, como '.json', também precisamos indicar no campo highlight
que quaisquer hiperlinks com sufixo de formato que ele retornar devem usar o sufixo '.html'.
Garantindo que nossos padrões de URL sejam nomeados
Se vamos ter uma API hiperlinkada, precisamos ter certeza de que nomeamos nossos padrões de URL. Vamos dar uma olhada em quais padrões de URL precisamos nomear.
- A raiz da nossa API se refere a 'user-list' e 'snippet-list'.
- Nosso serializador de snippet inclui um campo que se refere a 'snippet-highlight'.
- Nosso serializador de usuário inclui um campo que se refere a 'snippet-detail'.
- Nossos serializadores de snippet e usuário incluem campos 'url' que, por padrão, se referirão a
{model_name}-detail
, que neste caso serão 'snippet-detail' e 'user-detail'.
Depois de adicionar todos esses nomes em nosso URLconf, nosso arquivo final snippets/urls.py
deve ficar assim:
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
# API endpoints
urlpatterns = format_suffix_patterns([
path('', views.api_root),
path('snippets/',
views.SnippetList.as_view(),
name='snippet-list'),
path('snippets/<int:pk>/',
views.SnippetDetail.as_view(),
name='snippet-detail'),
path('snippets/<int:pk>/highlight/',
views.SnippetHighlight.as_view(),
name='snippet-highlight'),
path('users/',
views.UserList.as_view(),
name='user-list'),
path('users/<int:pk>/',
views.UserDetail.as_view(),
name='user-detail')
])
Adicionando paginação
As views de lista para usuários e snippets de código podem acabar retornando um grande número de instâncias, então realmente gostaríamos de ter certeza de que paginamos os resultados e permitimos que o cliente da API percorra cada uma das páginas individuais.
Podemos alterar o estilo de lista padrão para usar a paginação, modificando nosso arquivo tutorial/settings.py
ligeiramente. Adicione a seguinte configuração:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
Observe que as configurações no REST framework são todas colocadas em um único dicionário de configuração, chamado REST_FRAMEWORK
, o que ajuda a mantê-las bem separadas de suas outras configurações de projeto.
Também poderíamos personalizar o estilo de paginação se precisássemos, mas, neste caso, vamos apenas manter o padrão.
Navegando na API
Se abrirmos um navegador e navegarmos para a API, você descobrirá que agora pode navegar pela API simplesmente seguindo os links.
Você também poderá ver os links 'highlight' nas instâncias de snippet, que o levarão às representações HTML do código realçado.
Na parte 6 do tutorial, veremos como podemos usar ViewSets e Routers para reduzir a quantidade de código que precisamos para construir nossa API.