Entendeno Serializers no Django: A Ponte Entre Seus Modelos e a API Moderna Parte 2
- Published on
- Published on
- /6 mins read/---
Tutorial 2: Requisições e Respostas - Django REST Framework
A partir deste ponto, vamos realmente começar a cobrir o núcleo do REST framework. Vamos apresentar alguns blocos de construção essenciais.
Objetos Request
O REST framework introduz um objeto Request
que estende o HttpRequest
regular e fornece uma análise de requisição mais flexível. A funcionalidade principal do objeto Request
é o atributo request.data
, que é semelhante a request.POST
, mas mais útil para trabalhar com APIs Web.
request.POST # Lida apenas com dados de formulário. Funciona apenas para o método 'POST'.
request.data # Lida com dados arbitrários. Funciona para os métodos 'POST', 'PUT' e 'PATCH'.
Objetos Response
O REST framework também introduz um objeto Response
, que é um tipo de TemplateResponse
que recebe conteúdo não renderizado e usa negociação de conteúdo para determinar o tipo de conteúdo correto a ser retornado ao cliente.
return Response(data) # Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.
Códigos de Status
Usar códigos de status HTTP numéricos em suas views nem sempre torna a leitura óbvia, e é fácil não perceber se você errar um código de erro. O REST framework fornece identificadores mais explícitos para cada código de status, como HTTP_400_BAD_REQUEST
no módulo status
. É uma boa ideia usá-los em vez de usar identificadores numéricos.
Envolvendo Views de API
O REST framework fornece dois wrappers que você pode usar para escrever views de API.
- O decorador
@api_view
para trabalhar com views baseadas em funções. - A classe
APIView
para trabalhar com views baseadas em classes.
Esses wrappers fornecem algumas funcionalidades, como garantir que você receba instâncias de Request
em sua view e adicionar contexto a objetos Response
para que a negociação de conteúdo possa ser realizada.
Os wrappers também fornecem comportamentos como retornar respostas 405 Method Not Allowed
quando apropriado e lidar com quaisquer exceções ParseError
que ocorram ao acessar request.data
com entrada malformada.
Juntando Tudo
Ok, vamos em frente e começar a usar esses novos componentes para refatorar um pouco nossas views.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
Lista todos os trechos de código ou cria um novo trecho.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Nossa view de instância é uma melhoria em relação ao exemplo anterior. É um pouco mais concisa, e o código agora parece muito semelhante a se estivéssemos trabalhando com a API de Formulários. Também estamos usando códigos de status nomeados, o que torna os significados das respostas mais óbvios.
Aqui está a view para um snippet individual, no módulo views.py
.
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
Recupera, atualiza ou exclui um trecho de código.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Tudo isso deve parecer muito familiar - não é muito diferente de trabalhar com views regulares do Django.
Observe que não estamos mais vinculando explicitamente nossas requisições ou respostas a um determinado tipo de conteúdo. request.data
pode lidar com requisições json
de entrada, mas também pode lidar com outros formatos. Da mesma forma, estamos retornando objetos de resposta com dados, mas permitindo que o REST framework renderize a resposta no tipo de conteúdo correto para nós.
Adicionando Sufixos de Formato Opcionais aos Nossos URLs
Para aproveitar o fato de que nossas respostas não estão mais vinculadas a um único tipo de conteúdo, vamos adicionar suporte para sufixos de formato aos nossos endpoints da API. Usar sufixos de formato nos dá URLs que se referem explicitamente a um determinado formato e significa que nossa API será capaz de lidar com URLs como http://example.com/api/items/4.json
.
Comece adicionando um argumento de palavra-chave format
a ambas as views, assim:
def snippet_list(request, format=None):
e
def snippet_detail(request, pk, format=None):
Agora atualize o arquivo snippets/urls.py
ligeiramente, para anexar um conjunto de format_suffix_patterns
além dos URLs existentes.
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>/', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
Não precisamos necessariamente adicionar esses padrões de URL extras, mas isso nos dá uma maneira simples e limpa de nos referirmos a um formato específico.
Como Está Ficando?
Vá em frente e teste a API na linha de comando, como fizemos na parte 1 do tutorial. Tudo está funcionando de forma muito semelhante, embora tenhamos um tratamento de erros melhor se enviarmos requisições inválidas.
Podemos obter uma lista de todos os snippets, como antes.
http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
...
[
{
"id": 1,
"title": "",
"code": "foo = \"bar\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
},
{
"id": 2,
"title": "",
"code": "print(\"hello, world\")\n",
"linenos": false,
"language": "python",
"style": "friendly"
}
]
Podemos controlar o formato da resposta que recebemos, usando o cabeçalho Accept
:
http http://127.0.0.1:8000/snippets/ Accept:application/json # Solicita JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html # Solicita HTML
Ou anexando um sufixo de formato:
http http://127.0.0.1:8000/snippets.json # Sufixo JSON
http http://127.0.0.1:8000/snippets.api # Sufixo da API navegável
Da mesma forma, podemos controlar o formato da requisição que enviamos, usando o cabeçalho Content-Type
.
# POST usando dados de formulário
http --form POST http://127.0.0.1:8000/snippets/ code="print(123)"
{
"id": 3,
"title": "",
"code": "print(123)",
"linenos": false,
"language": "python",
"style": "friendly"
}
# POST usando JSON
http --json POST http://127.0.0.1:8000/snippets/ code="print(456)"
{
"id": 4,
"title": "",
"code": "print(456)",
"linenos": false,
"language": "python",
"style": "friendly"
}
Se você adicionar uma opção --debug
às requisições http acima, poderá ver o tipo de requisição nos cabeçalhos da requisição.
Agora vá e abra a API em um navegador da web, visitando http://127.0.0.1:8000/snippets/
.
Navegabilidade
Como a API escolhe o tipo de conteúdo da resposta com base na requisição do cliente, ela retornará, por padrão, uma representação formatada em HTML do recurso quando esse recurso for solicitado por um navegador da web. Isso permite que a API retorne uma representação HTML totalmente navegável na web.
Ter uma API navegável na web é uma grande vantagem de usabilidade e torna o desenvolvimento e o uso da sua API muito mais fáceis. Também reduz drasticamente a barreira de entrada para outros desenvolvedores que desejam inspecionar e trabalhar com sua API.
Consulte o tópico API navegável (substitua por um link real) para obter mais informações sobre o recurso de API navegável e como personalizá-lo.
O Que Vem a Seguir?
No tutorial parte 3 (substitua por um link real), começaremos a usar views baseadas em classes e veremos como as views genéricas reduzem a quantidade de código que precisamos escrever.