Em uma frase: Até agora a cozinha servia só no salão (admin); agora montamos o delivery em JSON para qualquer site ou app pedir. Pergunta guia: Como o catálogo vira um serviço que qualquer tela chama e como a API se defende de pedido ruim?
Slides visuais da lição F04 · NotebookLM
Material multimídia da lição
APIs com Django REST Framework (DRF)
Deck visual cobrindo Serializer, ViewSet, Router e os status HTTP (200, 201, 400, 404, 405, 409).
Slides visuais gerados para fixação. O texto pesquisável e os comandos completos permanecem logo abaixo na lição.
Errar uma previsão mostra exatamente onde está a lacuna. Chute mentalmente ou no papel antes de revelar a resposta do professor.
1. Um cliente envia {'nome': '', 'preco': 'abc'} para criar um produto. O que o servidor responde?
2. Um pedido deve aceitar {'status': 'confirmado'} vindo do navegador na criação?
1. O trio do DRF: Serializer, ViewSet e Router
No Django REST Framework, três peças trabalham juntas: o Serializer (conferente que valida e converte para JSON), o ViewSet (cozinheiro com as ações prontas de listar, criar, detalhar, editar e apagar) e o Router (garçom que gera todas as URLs automaticamente).
Ação de compra com baixa condicional de estoque (views.py)
@action(detail=True, methods=["post"])
def comprar(self, request, pk=None):
qtd = int(request.data.get("quantidade", 1))
if qtd <= 0:
return Response({"erro": "Quantidade deve ser maior que zero."}, status=400)
atualizados = Produto.objects.filter(
pk=pk, estoque__gte=qtd
).update(estoque=F("estoque") - qtd)
if atualizados == 0:
return Response({"erro": "Estoque insuficiente."}, status=409)
return Response({"ok": True}, status=200)
estoque__gte=qtd — A verificação e a subtração acontecem juntas em um único UPDATE no banco
status=409 — Conflict: o formato do pedido estava certo (não é 400), mas conflita com o estoque atual
Laboratório · Status HTTP na prática
Provoque cada código de resposta da API (200, 201, 400, 404, 405, 409)
O que o professor destaca: Consulta bem-sucedida. O servidor entregou a lista em JSON sem alterar nada na despensa.
Quebre de propósito
Remover o filtro estoque__gte=qtd do UPDATE
Experimento: Trocar Produto.objects.filter(pk=pk, estoque__gte=qtd).update(...) por uma leitura seguida de save().
O que falha: Se duas requisições chegarem juntas para a última unidade, ambas leem estoque = 1 e ambas vendem! Ou o CheckConstraint do banco estoura erro 500.
Por que a regra existe: Baixa de estoque em marketplace precisa de atualização condicional atômica no banco, retornando 409 Conflict quando não há saldo.
Armadilhas comuns (errado × certo)
Errado
Certo
Por quê
Retornar 200 OK com { 'erro': '...' } no corpo
Usar o código de status HTTP adequado (400, 404, 409)
Clientes e ferramentas de monitoramento olham primeiro o status HTTP.
Deixar campos de status ou total editáveis pelo cliente
Declarar read_only_fields no Serializer e calcular no servidor
Evita fraude de preço e de pagamento.
Mão na massa
Exercício com pista e gabarito
Qual é a diferença prática entre receber um 400 Bad Request e um 409 Conflict ao tentar comprar?
Ver pista
Um diz respeito ao formato/validade dos dados; o outro ao estado atual do recurso no banco.
Ver gabarito
400 significa que o cliente mandou dados malformados ou inválidos (ex.: quantidade = -2 ou texto no lugar de número). 409 significa que os dados são válidos, mas conflitam com o estado atual (ex.: estoque insuficiente).
Quiz da lição
Quiz com explicação de cada erro
Revisão espaçada · 1, 3 e 7 dias
Fichas de revisão (clique para virar)
Se você só lembrar de 3 coisas desta lição:
Serializer valida a entrada e embala a saída; ViewSet executa; Router cria os endereços.
Use códigos HTTP honestos: 200, 201, 400, 404, 405 e 409 contam exatamente o que aconteceu.
Nunca confie em preço ou status vindo do cliente: use read_only_fields e recalcule no servidor.