<h2>Serializers: A Base da Transformação de Dados</h2>
<p>Um serializer no Django REST Framework é uma classe responsável por converter dados complexos (como instâncias de modelos Django) em tipos primitivos Python que possam ser renderizados em JSON, XML ou outros formatos. Além disso, serializers realizam a desserialização, transformando dados vindos do cliente em objetos Django validados. Pense neles como um intermediário entre seu banco de dados e a API — garantindo que os dados estejam sempre no formato correto e validados antes de qualquer operação.</p>
<p>A razão pela qual você precisa de serializers é simples: modelos Django não sabem como se representar em JSON por padrão. Um serializer define quais campos de um modelo devem ser expostos, como devem ser transformados e quais validações aplicar. Existem dois tipos principais: <code>Serializer</code> (para dados genéricos) e <code>ModelSerializer</code> (para trabalhar diretamente com modelos Django).</p>
<pre><code class="language-python"># models.py
from django.db import models
class Livro(models.Model):
titulo = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
ano_publicacao = models.IntegerField()
preco = models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return self.titulo
serializers.py
from rest_framework import serializers
from .models import Livro
class LivroSerializer(serializers.ModelSerializer):
class Meta:
model = Livro
fields = ['id', 'titulo', 'autor', 'ano_publicacao', 'preco']</code></pre>
<p>Neste exemplo, o <code>LivroSerializer</code> herda de <code>ModelSerializer</code>, que automaticamente cria campos baseado no modelo. Quando você retorna uma instância de <code>Livro</code> através deste serializer, ela é convertida em um dicionário Python que pode ser renderizado como JSON. O framework automaticamente mapeia tipos Django (<code>CharField</code>, <code>DecimalField</code>, etc.) para tipos de serializers (<code>CharField</code>, <code>DecimalField</code>, etc.).</p>
<h3>Validações Customizadas</h3>
<p>Validações são cruciais para garantir a integridade dos dados. Você pode adicionar validações em nível de campo ou em nível de objeto.</p>
<pre><code class="language-python">from rest_framework import serializers
from .models import Livro
class LivroSerializer(serializers.ModelSerializer):
Validação em nível de campo
ano_publicacao = serializers.IntegerField(
min_value=1000,
max_value=2099,
error_messages={
'min_value': 'Ano não pode ser anterior a 1000',
'max_value': 'Ano não pode ser posterior a 2099'
}
)
class Meta:
model = Livro
fields = ['id', 'titulo', 'autor', 'ano_publicacao', 'preco']
Validação em nível de objeto
def validate(self, data):
if data.get('ano_publicacao') and data['ano_publicacao'] > 2024:
raise serializers.ValidationError(
"O ano de publicação não pode ser no futuro."
)
return data
Validação de campo específico
def validate_titulo(self, value):
if len(value) < 3:
raise serializers.ValidationError("O título deve ter pelo menos 3 caracteres.")
return value</code></pre>
<p>Aqui você vê três níveis de validação: no campo <code>ano_publicacao</code> com <code>min_value</code> e <code>max_value</code>, na validação geral do objeto com <code>validate()</code>, e na validação específica do título com <code>validate_titulo()</code>. Essas validações são executadas automaticamente quando você chama <code>serializer.is_valid()</code>.</p>
<h2>ViewSets: Estruturando suas Endpoints</h2>
<p>Um ViewSet é uma classe que combina a lógica para operações CRUD (Create, Read, Update, Delete) em um único lugar. Em vez de escrever quatro views diferentes para listar, recuperar, criar e atualizar objetos, você escreve um ViewSet que o framework expande automaticamente em múltiplas rotas. Isso reduz drasticamente o código duplicado e deixa sua API mais organizada.</p>
<p>O <code>ModelViewSet</code> é o tipo mais comum e fornece implementações prontas para todas as operações padrão. Ele herda de várias classes mixin que implementam <code>list()</code>, <code>create()</code>, <code>retrieve()</code>, <code>update()</code>, <code>partial_update()</code> e <code>destroy()</code>. Você pode sobrescrever qualquer um desses métodos para customizar o comportamento.</p>
<pre><code class="language-python"># views.py
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import Livro
from .serializers import LivroSerializer
class LivroViewSet(viewsets.ModelViewSet):
queryset = Livro.objects.all()
serializer_class = LivroSerializer
def perform_create(self, serializer):
"""Hook executado após validação bem-sucedida antes de salvar"""
print(f"Criando livro: {serializer.validated_data['titulo']}")
serializer.save()
def perform_update(self, serializer):
"""Hook executado após validação bem-sucedida antes de atualizar"""
print(f"Atualizando livro: {serializer.validated_data['titulo']}")
serializer.save()
@action(detail=False, methods=['get'])
def por_ano(self, request):
"""Endpoint customizado para listar livros por ano"""
ano = request.query_params.get('ano')
if not ano:
return Response({'erro': 'Parâmetro ano é obrigatório'}, status=400)
livros = self.queryset.filter(ano_publicacao=ano)
serializer = self.get_serializer(livros, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def aplicar_desconto(self, request, pk=None):
"""Endpoint customizado para aplicar desconto a um livro específico"""
livro = self.get_object()
percentual = request.data.get('percentual', 0)
if not isinstance(percentual, (int, float)) or percentual < 0 or percentual > 100:
return Response({'erro': 'Percentual inválido'}, status=400)
livro.preco = livro.preco * (1 - percentual / 100)
livro.save()
serializer = self.get_serializer(livro)
return Response(serializer.data)</code></pre>
<p>Neste exemplo, você vê como um <code>ModelViewSet</code> funciona. Automaticamente, ele cria os endpoints: <code>GET /livros/</code> (listar), <code>POST /livros/</code> (criar), <code>GET /livros/{id}/</code> (detalhe), <code>PUT /livros/{id}/</code> (atualizar completo), <code>PATCH /livros/{id}/</code> (atualização parcial) e <code>DELETE /livros/{id}/</code> (deletar). Os métodos <code>perform_create()</code> e <code>perform_update()</code> são hooks que você sobrescreve para adicionar lógica antes de salvar. Os decoradores <code>@action</code> criam endpoints customizados: <code>GET /livros/por_ano/?ano=2024</code> e <code>POST /livros/{id}/aplicar_desconto/</code>.</p>
<h3>Roteamento e Registro</h3>
<p>O roteamento conecta seus ViewSets às URLs. O <code>SimpleRouter</code> do DRF automatiza esse processo, identificando todas as actions do ViewSet e criando as rotas apropriadas.</p>
<pre><code class="language-python"># urls.py
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import LivroViewSet
router = SimpleRouter()
router.register(r'livros', LivroViewSet, basename='livro')
urlpatterns = [
path('api/', include(router.urls)),
]</code></pre>
<p>Esse código registra o <code>LivroViewSet</code> na URL base <code>/api/livros/</code>. O router automaticamente cria todas as rotas necessárias. Se você precisar de mais controle, pode usar <code>DefaultRouter</code>, que adiciona uma view raiz que lista todos os endpoints disponíveis.</p>
<h2>Autenticação e Permissões</h2>
<p>Autenticação é o processo de verificar quem o usuário é; permissões determinam o que ele pode fazer. Sem esses dois componentes, sua API seria insegura e acessível a qualquer um. Django REST Framework oferece múltiplas estratégias de autenticação: Token, JWT, Session, OAuth2, etc. Neste artigo, focaremos em Token Authentication e JWT, que são as mais comuns em APIs modernas.</p>
<h3>Token Authentication</h3>
<p>Token Authentication é simples: o cliente envia um token em cada requisição no header <code>Authorization: Token <seu_token></code>. O servidor valida esse token e autoriza a requisição se o token for válido e pertencer a um usuário autenticado.</p>
<pre><code class="language-python"># settings.py
INSTALLED_APPS = [
... outras apps
'rest_framework',
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
models.py (opcional, para criar tokens automaticamente)
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=User)
def criar_token_usuario(sender, instance, created, **kwargs):
if created:
Token.objects.create(user=instance)
views.py
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework import viewsets, permissions
from .models import Livro
from .serializers import LivroSerializer
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
"""Endpoint para obter token de autenticação"""
return obtain_auth_token(request)
class LivroViewSet(viewsets.ModelViewSet):
queryset = Livro.objects.all()
serializer_class = LivroSerializer
permission_classes = [permissions.IsAuthenticated]</code></pre>
<p>Com essa configuração, qualquer requisição para endpoints do <code>LivroViewSet</code> requer um token válido. O cliente obtém o token fazendo uma requisição POST para <code>/login/</code> com suas credenciais. Depois, inclui esse token em todas as requisições subsequentes: <code>curl -H "Authorization: Token abc123xyz" http://localhost:8000/api/livros/</code>.</p>
<h3>Permissões Granulares</h3>
<p>Frequentemente, você quer diferentes níveis de acesso. Um usuário comum talvez possa ler livros mas não deletá-los. Um editor pode criar e atualizar. Um admin pode fazer tudo. Django REST Framework oferece classes de permissão prontas como <code>IsAuthenticated</code>, <code>IsAdminUser</code>, <code>IsAuthenticatedOrReadOnly</code>. Você também pode criar suas próprias.</p>
<pre><code class="language-python">from rest_framework import permissions
class EhAutorOuLeitura(permissions.BasePermission):
"""
Permissão customizada: qualquer um pode ler,
mas apenas o autor pode editar/deletar
"""
def has_object_permission(self, request, view, obj):
Leitura é permitida para qualquer requisição
if request.method in permissions.SAFE_METHODS:
return True
Escrita apenas para o autor do livro
return obj.autor_id == request.user.id
class LivroViewSet(viewsets.ModelViewSet):
queryset = Livro.objects.all()
serializer_class = LivroSerializer
permission_classes = [permissions.IsAuthenticated, EhAutorOuLeitura]
def perform_create(self, serializer):
Automaticamente associa o livro ao usuário logado
serializer.save(autor_id=self.request.user.id)</code></pre>
<p>Aqui, a classe <code>EhAutorOuLeitura</code> verifica se o método HTTP é seguro (GET, HEAD, OPTIONS) — se for, permite. Se não for, verifica se o usuário é o dono do objeto. Isso permite leitura pública mas escrita restrita. Você pode encadear múltiplas permissões na lista <code>permission_classes</code>, e a requisição é autorizada apenas se todas as permissões passarem.</p>
<h3>JWT (JSON Web Tokens)</h3>
<p>JWT é uma alternativa mais moderna ao Token simples. Um JWT é um token autossuficiente que contém informações codificadas sobre o usuário e expira após um tempo. Isso é mais seguro porque o servidor não precisa consultar o banco de dados a cada requisição.</p>
<pre><code class="language-python"># settings.py
INSTALLED_APPS = [
... outras apps
'rest_framework',
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
}
urls.py
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from rest_framework.routers import SimpleRouter
from .views import LivroViewSet
router = SimpleRouter()
router.register(r'livros', LivroViewSet, basename='livro')
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('api/', include(router.urls)),
]</code></pre>
<p>O cliente faz um POST para <code>/api/token/</code> com username e password, recebendo um <code>access_token</code> e um <code>refresh_token</code>. O <code>access_token</code> é válido por 5 minutos; após expirar, o cliente usa o <code>refresh_token</code> em <code>/api/token/refresh/</code> para obter um novo <code>access_token</code>. Isso oferece melhor segurança porque tokens de curta duração reduzem o impacto se um token for comprometido.</p>
<pre><code class="language-python"># Exemplo de uso no cliente
import requests
import json
1. Obter tokens
response = requests.post('http://localhost:8000/api/token/', data={
'username': 'usuario',
'password': 'senha123'
})
tokens = response.json()
access_token = tokens['access']
2. Usar o access_token em requisições
headers = {'Authorization': f'Bearer {access_token}'}
response = requests.get('http://localhost:8000/api/livros/', headers=headers)
print(response.json())
3. Se expirar, usar refresh_token
response = requests.post('http://localhost:8000/api/token/refresh/', data={
'refresh': tokens['refresh']
})
new_access_token = response.json()['access']</code></pre>
<h2>Conclusão</h2>
<p>Nesta aula, você aprendeu os três pilares do Django REST Framework: <strong>Serializers</strong> transformam dados entre representações (banco de dados ↔ JSON) e validam integridade; <strong>ViewSets</strong> eliminam duplicação de código ao fornecer CRUD automático com hooks para customização; <strong>Autenticação e Permissões</strong> protegem sua API garantindo que apenas usuários autorizados acessem recursos apropriados. Esses três conceitos trabalham juntos: um ViewSet usa um Serializer para processar dados e aplica Permissões para controlar acesso. Domine esses fundamentos e você construirá APIs robustas, seguras e mantíveis com Django REST Framework.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.django-rest-framework.org/" target="_blank" rel="noopener noreferrer">Django REST Framework - Official Documentation</a></li>
<li><a href="https://www.django-rest-framework.org/api-guide/serializers/" target="_blank" rel="noopener noreferrer">Django REST Framework - Serializers</a></li>
<li><a href="https://www.django-rest-framework.org/api-guide/viewsets/" target="_blank" rel="noopener noreferrer">Django REST Framework - ViewSets</a></li>
<li><a href="https://www.django-rest-framework.org/api-guide/authentication/" target="_blank" rel="noopener noreferrer">Django REST Framework - Authentication & Permissions</a></li>
<li><a href="https://django-rest-framework-simplejwt.readthedocs.io/" target="_blank" rel="noopener noreferrer">Simple JWT - JSON Web Tokens for Django</a></li>
</ul>
<p><!-- FIM --></p>