DESENVOLVIMENTO WEB II
OBJETIVO GERAL
Capacitar o aluno a projetar, desenvolver, documentar e publicar APIs REST, utilizando Django + Django REST Framework como framework de referência, mas compreendendo conceitos aplicáveis a qualquer stack de desenvolvimento.
OBJETIVOS ESPECÍFICOS
- Entender os fundamentos de APIs, HTTP e REST.
- Implementar CRUDs com Django REST Framework utilizando
ModelViewSeteModelSerializer. - Trabalhar com autenticação, permissões, filtros, paginação e upload de arquivos.
- Documentar APIs utilizando ferramentas compatíveis com o padrão OpenAPI (Swagger).
- Publicar projetos em serviços de deploy modernos (Render, Supabase, AWS, etc.).
- Trabalhar com versionamento de código e boas práticas de commits no GitHub.
- Desenvolver projeto final com requisitos mínimos, mas liberdade para escolha de tecnologias.
LINKS ÚTEIS
- Django:
- Roadmap de backend
- Vídeos:
- Cursos:
- FastAPI do ZERO
- Aplicação Front-end de Músicas: https://github.com/marrcandre/song-vue
Objetivo da Aula
Compreender o que é uma API, como funciona o protocolo HTTP e como interagir com APIs públicas que trazem informações úteis para o dia a dia.
Conteúdo
1. Apresentação da disciplina
- Ementa, objetivos e formas de avaliação.
- Projeto final e avaliação teórica no meio do semestre.
- Expectativas e forma de trabalho (teoria + prática).
2. O que é uma API
API (Application Programming Interface) é um conjunto de regras e definições que permite que sistemas diferentes se comuniquem. Ela define como um sistema pode ser usado por outro.
- Exemplos do dia a dia:
- Aplicativo de clima pegando dados de um serviço meteorológico.
- Aplicativo de transporte usando mapas do Google.
- Sites de e-commerce consultando meios de pagamento.
- API Web: acessada via internet, geralmente usando o protocolo HTTP.
- Cliente (quem faz a requisição) ↔ Servidor (quem responde com os dados).
3. REST – Princípios básicos
REST (Representational State Transfer) é um conjunto de boas práticas para criar APIs web. Principais características:
- Stateless – cada requisição contém todas as informações necessárias; o servidor não guarda o “estado” do cliente.
- Recursos identificados por URL – cada tipo de dado tem um endereço único (ex:
/usuarios/1para o usuário com id 1). - Operações com métodos HTTP – ações claras para cada tipo de operação.
- Formato de dados padronizado – normalmente JSON (mas pode incluir outros formatos, como XML).
Principais métodos HTTP e uso comum:
- GET → Ler dados.
Ex:
GET /usuarios - POST → Criar novos dados.
Ex:
POST /usuarios - PUT → Atualizar completamente um recurso existente.
Ex:
PUT /usuarios/1 - PATCH → Atualizar parcialmente um recurso existente.
Ex:
PATCH /usuarios/1 - DELETE → Remover dados.
Ex:
DELETE /usuarios/1
4. Status codes HTTP
O servidor sempre responde com um código numérico indicando o resultado da requisição.
Principais categorias:
- 1xx – Informativo (raramente usado diretamente pelo dev)
- 2xx – Sucesso
200 OK→ Requisição bem-sucedida201 Created→ Recurso criado com sucesso204 No Content→ Sucesso sem conteúdo no corpo da resposta
- 3xx – Redirecionamento
301 Moved Permanently302 Found
- 4xx – Erros do cliente
400 Bad Request→ Requisição inválida401 Unauthorized→ Autenticação necessária403 Forbidden→ Acesso negado404 Not Found→ Recurso não encontrado409 Conflict→ Conflito de requisição
- 5xx – Erros do servidor
500 Internal Server Error503 Service Unavailable
5. Demonstração – APIs Públicas Úteis
Vamos explorar APIs reais que trazem dados práticos para Araquari/SC.
-
Consultar um CEP (ViaCEP): https://viacep.com.br/ws/89211120/json/
-
Cotação do dólar (AwesomeAPI): https://economia.awesomeapi.com.br/json/last/USD-BRL
-
Previsão do tempo (Open-Meteo):
- Latitude/Longitude de Araquari: -26.3747, -48.7181 https://api.open-meteo.com/v1/forecast?latitude=-26.3747&longitude=-48.7181¤t_weather=true
Cada requisição será feita usando navegador, Postman e Thunder Client, para comparar resultados.
Atividade Prática
Objetivo: Usar ou Thunder Client para explorar APIs públicas úteis.
- Escolher uma API da lista acima.
- Fazer uma requisição GET.
- Registrar:
- URL utilizada
- Método HTTP
- Status code
- Conteúdo JSON retornado
Atividades de Fixação
Escolha uma API pública diferente das apresentadas e:
- Faça 3 requisições (GET, POST e DELETE, se suportado).
- Salve prints das requisições e respostas.
- Anote status code e conteúdo retornado.
Sugestões:
- BrasilAPI (CNPJs, feriados, dados de bancos)
- IBGE – Localidades
- Cat Facts (fatos aleatórios sobre gatos)
- JSONPlaceholder (API falsa para testes)
Recursos e Links
Objetivo
Compreender como estruturar e testar uma API simples em duas tecnologias diferentes (Python + FastAPI e Node.js + Express), reforçando que os conceitos de HTTP e REST são independentes da stack utilizada.
Conteúdo
1. Revisão rápida
- Métodos HTTP (GET, POST, PUT/PATCH, DELETE).
- Status codes.
- Estrutura de uma resposta JSON.
2. Criando uma API com FastAPI (Python)
Objetivo: Montar um servidor que responda a requisições de teste.
Instalação
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install fastapi uvicornCódigo - main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Modelo para POST
class Item(BaseModel):
nome: str
preco: float
# Rota GET
@app.get("/produtos")
def listar_produtos():
return [
{"id": 1, "nome": "Notebook", "preco": 3500},
{"id": 2, "nome": "Mouse", "preco": 80},
{"id": 3, "nome": "Teclado", "preco": 150},
{"id": 4, "nome": "Monitor", "preco": 1200},
{"id": 5, "nome": "Impressora", "preco": 300},
]
# Rota POST
@app.post("/produtos")
def criar_produto(item: Item):
return {"mensagem": "Produto criado com sucesso", "dados": item}
# Rodar servidor:
# uvicorn main:app --reloadRodar servidor:
uvicorn main:app --reloadExplicação do código
- FastAPI: Framework para construção de APIs em Python.
- Pydantic: Biblioteca para validação de dados e criação de modelos.
- Item: Modelo que representa um produto, com campos para nome e preço.
- Rotas:
GET /produtos: Retorna uma lista de produtos.POST /produtos: Cria um novo produto a partir dos dados enviados no corpo da requisição.
- Execução: Para rodar o servidor, utilize o comando
uvicorn main:app --reload. - Testes: Utilize ferramentas como Postman ou Thunder Client para testar as rotas da API.
- Documentação: Acesse a documentação automática gerada pelo FastAPI em
http://localhost:8000/docs. - Exemplos de Requisições:
GET /produtos: Retorna todos os produtos.POST /produtos: Cria um novo produto.
Um exemplo mais completo
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Modelo para itens
class Item(BaseModel):
id: int
nome: str
preco: float
# Modelo para POST
class ItemInput(BaseModel):
nome: str
preco: float
# Modelo para resposta de POST
class ItemInputResponse(BaseModel):
message: str
dados: Item
# Lista de itens em memória
items = [
Item(id=1, nome="Notebook", preco=3500.00),
Item(id=2, nome="Mouse", preco=80.00),
Item(id=3, nome="Teclado", preco=150.00),
Item(id=4, nome="Monitor", preco=1200.00),
Item(id=5, nome="Impressora", preco=300.00),
]
# Rota GET
@app.get("/produtos", response_model=list[Item])
def listar_produtos():
return [
{"id": item.id, "nome": item.nome, "preco": item.preco} for item in items
]
# Rota POST
@app.post("/produtos", response_model=ItemInputResponse)
def criar_produto(item: ItemInput):
novo_id = max(item.id for item in items) + 1
novo_item = Item(id=novo_id, **item.model_dump())
items.append(novo_item)
response = ItemInputResponse(message="Produto criado com sucesso", dados=novo_item)
return response.model_dump()
# Rodar servidor:
# uvicorn main:app --reloadDiferenças neste código
- Modelos: Foram adicionados modelos mais específicos para entrada e saída de dados, utilizando o Pydantic.
- Rota GET: A rota GET agora utiliza o modelo de resposta para garantir que a estrutura dos dados retornados esteja correta.
- Rota POST: A rota POST agora utiliza um modelo de entrada e um modelo de resposta, melhorando a validação e a documentação automática da API.
- Lista em memória: A lista de itens é mantida em memória, permitindo a adição de novos produtos via POST.
- Validação: O Pydantic garante que os dados enviados nas requisições estejam no formato correto, retornando erros de validação automaticamente quando necessário.
- Documentação: A documentação automática gerada pelo FastAPI é atualizada com base nos modelos utilizados, facilitando a compreensão da API.
3. Criando uma API com Node.js + Express
Objetivo: Implementar o mesmo comportamento usando JavaScript.
Instalação
npm init -y
npm install expressCódigo - index.js
const express = require('express');
const app = express();
app.use(express.json());
// Rota GET
app.get('/produtos', (req, res) => {
res.json([
{ id: 1, nome: 'Mouse', preco: 89.90 },
{ id: 2, nome: 'Teclado', preco: 199.90 }
]);
});
// Rota POST
app.post('/produtos', (req, res) => {
res.json({
mensagem: 'Produto criado com sucesso',
dados: req.body
});
});
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
// Rodar servidor:
// node index.jsRodar servidor:
node index.jsUm exemplo mais completo
const express = require('express');
const app = express();
app.use(express.json());
// "Banco de dados" em memória
let produtos = [
{ id: 1, nome: 'Teclado', preco: 199.90 },
{ id: 2, nome: 'Mouse', preco: 89.90 }
];
// Rota GET
app.get('/produtos', (req, res) => {
res.json([...produtos]); // cópia para evitar alteração acidental
});
// Rota POST
app.post('/produtos', (req, res) => {
const novoId = produtos.length > 0 ? Math.max(...produtos.map(p => p.id)) + 1 : 1;
const novoProduto = { id: novoId, ...req.body };
produtos.push(novoProduto);
res.status(201).json({
message: 'Produto criado com sucesso',
dados: novoProduto
});
});
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));Explicação do código
Temos agora o código Express com a mesma lógica do seu exemplo em FastAPI, inclusive mantendo:
- Lista de produtos em memória.
- POST que adiciona e retorna o novo item junto com mensagem.
- Geração automática de id.
Diferenças em relação ao FastAPI:
- No Express, não temos tipagem automática nem validação de entrada por padrão (equivalente ao que o Pydantic faz no FastAPI).
- A rota POST usa req.body diretamente, por isso seria interessante depois incluir uma validação para manter a mesma segurança de tipos.
- A estrutura da resposta (message + dados) foi mantida idêntica à versão Python para facilitar comparação lado a lado.
4. Testando com Postman / Thunder Client
- Fazer requisição GET para /produtos.
- Fazer requisição POST para /produtos com corpo JSON:
{
"nome": "Monitor",
"preco": 1299.90
}- Comparar:
- Estrutura da resposta.
- Status code retornado.
- Diferenças na implementação entre FastAPI e Express.
Discussão
- O conceito de rota, método e resposta é igual em ambas as stacks.
- Diferenças estão apenas na sintaxe e ferramentas.
- Importância de entender HTTP antes de aprender frameworks.
Atividade de Fixação
- Adicionar uma rota DELETE em ambas as implementações, removendo um produto pelo id.
- Testar no Postman ou Thunder Client e registrar o status code.
Objetivo
Ampliar o conhecimento sobre APIs explorando rotas dinâmicas com parâmetros e retornos mais elaborados em JSON, preparando o terreno para integração com banco de dados.
Revisão da aula anterior
Relembrar rapidamente:
- Diferença entre GET e POST.
- Corpo de requisição (body) e retorno (response).
- Conceito de status code e mensagens de resposta.
Conteúdo
1. Introdução aos parâmetros de rota e query
Conceitos:
-
Parâmetros de rota (path params): parte da URL que representa um dado variável.
- Ex.:
/produtos/123→123é o ID do produto.
- Ex.:
-
Parâmetros de query (query params): enviados na URL após
?para filtros, ordenação e paginação.- Ex.:
/produtos?categoria=eletronicos&ordem=preco.
- Ex.:
Boas práticas:
- Usar parâmetros de rota para identificar recursos.
- Usar parâmetros de query para filtragem, paginação e ordenação.
2. Demonstração prática – FastAPI
Exemplo de aplicação:
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
produtos = [
{"id": 1, "nome": "Notebook", "preco": 3500},
{"id": 2, "nome": "Mouse", "preco": 80},
{"id": 3, "nome": "Teclado", "preco": 150},
{"id": 4, "nome": "Monitor", "preco": 1200},
{"id": 5, "nome": "Impressora", "preco": 300},
]
@app.get("/produtos/{id_produto}")
def get_produto(id_produto: int):
for produto in produtos:
if produto["id"] == id_produto:
return produto
return {"erro": "Produto não encontrado"}
@app.get("/produtos")
def listar_produtos(categoria: Optional[str] = None):
# Como ainda não temos banco, o filtro é apenas ilustrativo
return produtosTestar no navegador e no Postman:
3. Demonstração prática – Node.js com Express
const produtos = [
{ id: 1, nome: 'Notebook', preco: 3500 },
{ id: 2, nome: 'Mouse', preco: 120 },
{ id: 3, nome: 'Teclado', preco: 250 }
];
app.get('/produtos/:id', (req, res) => {
const id = parseInt(req.params.id);
const produto = produtos.find(p => p.id === id);
if (produto) {
res.json(produto);
} else {
res.status(404).json({ erro: 'Produto não encontrado' });
}
});
app.get('/produtos', (req, res) => {
// Filtro fictício
res.json(produtos);
});
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});Testar no navegador e no Postman:
Exercício prático
- Criar uma rota que busque produtos com base em query params
min_precoemax_preco. - Implementar no FastAPI ou Express.
- Testar com Postman.
Objetivo da Aula:
Implementar operações completas de CRUD em APIs REST, utilizando FastAPI e Node.js + Express, reforçando conceitos de HTTP, rotas, status codes e resposta estruturada.
Revisão rápida
-
Conceitos revisados:
- Métodos HTTP: GET, POST, PUT, DELETE
- Status codes: 200, 201, 204, 404
- Estrutura de resposta JSON
-
Pergunta:
- "Como podemos manipular dados de uma lista de produtos usando apenas requisições HTTP?"
Conteúdo
1. CRUD completo com FastAPI
Código exemplo:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
# Modelo para itens
class Item(BaseModel):
id: int
nome: str
preco: float
# Modelo para entrada de dados (POST/PUT)
class ItemInput(BaseModel):
nome: str
preco: float
# Modelo para resposta de POST
class ItemInputResponse(BaseModel):
message: str
dados: Item
# Lista de itens em memória
items = [
Item(id=1, nome="Notebook", preco=3500.00),
Item(id=2, nome="Mouse", preco=80.00),
Item(id=3, nome="Teclado", preco=150.00),
Item(id=4, nome="Monitor", preco=1200.00),
Item(id=5, nome="Impressora", preco=300.00),
]
# Listar todos os produtos (GET)
@app.get("/produtos", response_model=list[Item])
def listar_produtos():
return items
# Listar produto por ID (GET)
@app.get("/produtos/{item_id}", response_model=Item)
def listar_produto_por_id(item_id: int):
for item in items:
if item.id == item_id:
return item
raise HTTPException(status_code=404, detail="Produto não encontrado")
# Criar novo produto (POST)
@app.post("/produtos", response_model=ItemInputResponse)
def criar_produto(item: ItemInput):
novo_id = max(item_existente.id for item_existente in items) + 1
novo_item = Item(id=novo_id, **item.model_dump())
items.append(novo_item)
return ItemInputResponse(message="Produto criado com sucesso", dados=novo_item)
# Atualizar produto (PUT)
@app.put("/produtos/{item_id}", response_model=ItemInputResponse)
def atualizar_produto(item_id: int, item_atualizado: ItemInput):
for i, item in enumerate(items):
if item.id == item_id:
items[i] = Item(id=item_id, **item_atualizado.model_dump())
return ItemInputResponse(message="Produto atualizado com sucesso", dados=items[i])
raise HTTPException(status_code=404, detail="Produto não encontrado")
# Remover produto (DELETE)
@app.delete("/produtos/{item_id}")
def remover_produto(item_id: int):
for i, item in enumerate(items):
if item.id == item_id:
items.pop(i)
return {"message": "Produto removido com sucesso"}
raise HTTPException(status_code=404, detail="Produto não encontrado")
# Rodar servidor:
# uvicorn aula4_crud_completo:app --reload
# Acesse a documentação da API em http://localhost:8000/docsDemonstração prática:
- Rodar o servidor com:
uvicorn main:app --reload - Testar todas as rotas usando Postman ou Thunder Client:
GET /produtos→ listar todosGET /produtos/1→ listar por idPOST /produtos→ criar novo produtoPUT /produtos/1→ atualizar produtoDELETE /produtos/1→ remover produto
2. CRUD completo com Express
Código exemplo:
const express = require('express');
const app = express();
app.use(express.json());
let produtos = [
{ id: 1, nome: "Notebook", preco: 3500.00 },
{ id: 2, nome: "Mouse", preco: 80.00 },
{ id: 3, nome: "Teclado", preco: 150.00 },
{ id: 4, nome: "Monitor", preco: 1200.00 },
{ id: 5, nome: "Impressora", preco: 300.00 }
];
app.get('/produtos', (req, res) => res.json(produtos));
app.get('/produtos/:id', (req, res) => {
const produto = produtos.find(p => p.id === parseInt(req.params.id));
if (!produto) return res.status(404).json({ error: "Produto não encontrado" });
res.json(produto);
});
app.post('/produtos', (req, res) => {
const { nome, preco } = req.body;
const novoId = produtos.length ? Math.max(...produtos.map(p => p.id)) + 1 : 1;
const novoProduto = { id: novoId, nome, preco };
produtos.push(novoProduto);
res.json({ message: "Produto criado com sucesso", dados: novoProduto });
});
app.put('/produtos/:id', (req, res) => {
const index = produtos.findIndex(p => p.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: "Produto não encontrado" });
produtos[index] = { id: parseInt(req.params.id), ...req.body };
res.json({ message: "Produto atualizado com sucesso", dados: produtos[index] });
});
app.delete('/produtos/:id', (req, res) => {
const index = produtos.findIndex(p => p.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: "Produto não encontrado" });
produtos.splice(index, 1);
res.json({ message: "Produto removido com sucesso" });
});
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));Demonstração prática:
- Rodar o servidor:
node index.js - Testar todas as rotas no Postman ou Thunder Client, da mesma forma que no FastAPI.
Discussão
- Comparar FastAPI e Express:
- Sintaxe diferente, mesma lógica de CRUD.
- Status codes, rotas e payloads JSON são iguais.
- FastAPI gera documentação automática; Express depende de ferramentas externas (Swagger, Postman Collection).
- Reforçar conceitos de HTTP, REST e boas práticas.
Atividade prática
- Adicionar no projeto:
- Filtrar produtos por min_preco e max_preco (query params).
- Implementar validação básica de dados (preço > 0, nome não vazio).
- Testar todas as rotas e anotar status codes e respostas.
Objetivo da Aula:
Implementar um CRUD completo com validação de dados, filtros, ordenação e paginação em FastAPI, reforçando conceitos de boas práticas em APIs REST.
Revisão rápida:
- CRUD básico da Aula anterior.
- Relembrar: métodos HTTP, status codes e resposta JSON.
- Pergunta:
- "Se nossa lista tiver 10 mil produtos, como facilitar a busca e listagem?"
Conteúdo
1. CRUD com validação
- Validação no Pydantic:
nomenão pode ser vazio,precodeve ser maior que zero. - Exemplo:
from pydantic import BaseModel, Field
class ItemInput(BaseModel):
nome: str = Field(..., min_length=1, description="Nome não pode ser vazio")
preco: float = Field(..., gt=0, description="Preço deve ser maior que zero")2. Listagem com filtros
min_precoemax_precocomo parâmetros de query.- Exemplo de rota:
@app.get("/produtos", response_model=list[Item])
def listar_produtos(min_preco: float | None = None, max_preco: float | None = None):
resultado = items
if min_preco is not None:
resultado = [item for item in resultado if item.preco >= min_preco]
if max_preco is not None:
resultado = [item for item in resultado if item.preco <= max_preco]
return resultado3. Ordenação
- Parâmetro
ordenar_por(nome | preco) eordem(asc | desc). - Exemplo:
@app.get("/produtos", response_model=list[Item])
def listar_produtos(
ordenar_por: str = "id",
ordem: str = "asc",
):
resultado = items
resultado.sort(key=lambda x: getattr(x, ordenar_por), reverse=(ordem == "desc"))4. Paginação
- Adicionar parâmetros
paginaepor_pagina. - Exemplo:
@app.get("/produtos", response_model=list[Item])
def listar_produtos(pagina: int = 0, por_pagina: int = 10):
return items[pagina * por_pagina : (pagina + 1) * por_pagina]5. Combinação dos recursos
- Filtros, paginação e ordenação juntos na mesma rota.
- Discussão:
- "O código está ficando repetitivo e verboso. Como frameworks mais completos (como DRF) ajudam a reduzir isso?"
Atividade prática
- Exercício:
- Implementar validação, filtros, ordenação e paginação juntos.
- Testar diferentes combinações no Swagger UI (http://localhost:8000/docs).
Conclusão
À medida em que vamos o sistema vai crescendo e vamos implementando mais recursos, é importante manter a organização e a clareza do código. O uso de frameworks e boas práticas de desenvolvimento pode ajudar a gerenciar a complexidade e a escalabilidade da aplicação.
Objetivo da Aula:
Implementar um CRUD completo com validação de dados, filtros, ordenação e paginação em Express, reforçando a comparação entre Python (FastAPI) e JavaScript (Node.js/Express).
Revisão rápida:
- CRUD básico com Express (Aula 4).
- Na Aula 5 fizemos isso no FastAPI com validações, filtros, ordenação e paginação.
- Pergunta para reflexão:
- "Qual código ficou mais verboso: FastAPI ou Express?"
Conteúdo
1. CRUD com validação
- No Express fazemos validações manualmente.
- Exemplo de validação no
POST:
if (!nome || nome.trim() === "") {
return res.status(400).json({ error: "O nome não pode ser vazio" });
}
if (typeof preco !== "number" || preco <= 0) {
return res.status(400).json({ error: "O preço deve ser maior que zero" });
}2. Listagem com filtros
- Podemos passar parâmetros de query:
min_preco→ filtra produtos com preço mínimo.max_preco→ filtra produtos com preço máximo.
Exemplo:
if (min_preco) resultado = resultado.filter(p => p.preco >= parseFloat(min_preco));
if (max_preco) resultado = resultado.filter(p => p.preco <= parseFloat(max_preco));3. Ordenação
ordenar_por→ nome ou preço.ordem→ asc ou desc.
Exemplo:
if (ordenar_por) {
resultado.sort((a, b) => {
if (a[ordenar_por] < b[ordenar_por]) return ordem === "desc" ? 1 : -1;
if (a[ordenar_por] > b[ordenar_por]) return ordem === "desc" ? -1 : 1;
return 0;
});
}4. Paginação
- Usamos
paginaepor_pagina. - Exemplo:
pagina = parseInt(pagina) || 0;
por_pagina = parseInt(por_pagina) || resultado.length;
resultado = resultado.slice(pagina, pagina + por_pagina);Atividade prática
-
Testar a API com os seguintes endpoints:
- Listar todos os produtos:
http://localhost:8000/produtos - Ordenar por preço (crescente):
http://localhost:8000/produtos?ordenar_por=preco&ordem=asc - Ordenar por nome (decrescente):
http://localhost:8000/produtos?ordenar_por=nome&ordem=desc - Filtrar por preço mínimo:
http://localhost:8000/produtos?min_preco=100 - Filtrar por preço máximo:
http://localhost:8000/produtos?max_preco=1000 - Filtrar por preço mínimo e máximo:
http://localhost:8000/produtos?min_preco=100&max_preco=1000 - Paginação:
http://localhost:8000/produtos?pagina=1&por_pagina=2
- Listar todos os produtos:
Conclusão
- No FastAPI (Python), usamos Pydantic para validação automática.
- No Express (Node.js), precisamos implementar manualmente as validações.
- O código em Express tende a ser mais verboso e repetitivo, mas também é mais flexível.
Objetivo:
Apresentar o Django como framework web completo, comparando-o com FastAPI e Express. Criar a primeira aplicação e compreender sua estrutura de projeto.
Conteúdo da Aula:
1. Contextualização: o que é Django?
- Django é um framework web full-stack para Python, criado em 2005.
- Filosofia: “O framework web para perfeccionistas com prazos”.
- Diferença de abordagem:
- FastAPI/Express → minimalistas, você escolhe os pacotes.
- Django → traz tudo pronto (ORM, templates, autenticação, administração, rotas, segurança).
2. Preparando o ambiente
Criar pasta do projeto:
mkdir django_intro && cd django_intro
python -m venv .venv
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows
pip install --upgrade pipInstalar Django:
pip install djangoSalvar dependências:
pip freeze > requirements.txtConferir versão:
python -m django --version3. Criando o primeiro projeto Django
Criar projeto:
django-admin startproject config .Entrar no vscode
code .Estrutura criada:
config/
manage.py
config/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
Comparação:
- Express/FastAPI: 1 arquivo principal (
app.js/main.py). - Django: projeto organizado em múltiplos arquivos, com configurações centrais (
settings.py) e entrada administrativa (manage.py).
Rodar servidor:
python manage.py runserverAcessar http://127.0.0.1:8000.
4. Criando a primeira aplicação Django
Criar app:
python manage.py startapp produtosEstrutura criada:
produtos/
admin.py
apps.py
models.py
tests.py
views.py
Registrar app em settings.py:
INSTALLED_APPS = [
...
'produtos',
]Criar primeira view (produtos/views.py):
from django.http import HttpResponse
def home(request):
return HttpResponse("Bem-vindo à BSI4 Store!")Mapear rota (config/urls.py):
from django.contrib import admin
from django.urls import path
from produtos.views import home
urlpatterns = [
path('admin/', admin.site.urls),
path('', home),
]Agora a raiz (/) exibe: “Bem-vindo à BSI4 Store!”.
Atividade prática
- Criar uma nova view
sobreemprodutos/views.pyretornando “Loja feita em Django”. - Mapear rota
/sobre. - Testar no navegador.
Fechamento e comparação
- FastAPI/Express: frameworks leves, onde tudo precisa ser adicionado.
- Django: já traz camadas completas: ORM, admin, autenticação, segurança, templates.
- Demonstração rápida: mostrar a página do admin (criar superusuário):
python manage.py createsuperuser- Antes, é necessário criar as tabelas do banco (SQLite por padrão):
python manage.py migrateEntrar em /admin/.
Resumo da Aula
- O Django é mais opinativo e estruturado do que FastAPI/Express.
- Criamos nosso primeiro projeto e app.
- Configuramos rotas e views simples.
- Exploramos o admin como diferencial.
Objetivo:
Entender como o Django lida com persistência de dados através de models e migrações, e aprender a cadastrar e visualizar dados no admin.
1. Introdução
Para armazenar dados de forma eficiente e organizada, o Django utiliza um sistema de ORM (Object Relational Mapper). Um ORM (Object Relational Mapper) é uma ferramenta que permite interagir com bancos de dados utilizando a linguagem de programação, sem a necessidade de escrever SQL diretamente.
No FastAPI/Express, a modelagem de dados é feita via ORMs externos (SQLAlchemy, Prisma, Mongoose).
No Django, o ORM já está integrado ao framework.
Cada app tem seus próprios models.py, que descrevem as tabelas do banco.
2. Criando a primeira Model
No arquivo produtos/models.py:
from django.db import models
class Produto(models.Model):
nome = models.CharField(max_length=100)
descricao = models.TextField(blank=True)
preco = models.DecimalField(max_digits=8, decimal_places=2)
estoque = models.IntegerField(default=0)
criado_em = models.DateTimeField(auto_now_add=True)
atualizado_em = models.DateTimeField(auto_now=True)
def __str__(self):
return self.nomeExplicação dos campos
CharField: texto curto, exigemax_length.TextField: textos longos (ex: descrição).DecimalField: valores numéricos precisos (bom para dinheiro).IntegerField: números inteiros.DateTimeField: datas automáticas (auto_now_add= quando criado,auto_now= quando alterado).__str__: como o objeto aparece no admin.
3. Criando e aplicando migrações
Comandos no terminal:
python manage.py makemigrations
python manage.py migratemakemigrations: cria os arquivos de migração baseados nas models.migrate: aplica ao banco de dados.
Verifique no diretório produtos/migrations/ que foi criado um arquivo.
4. Registrando a Model no Admin
No arquivo produtos/admin.py:
from django.contrib import admin
from .models import Produto
@admin.register(Produto)
class ProdutoAdmin(admin.ModelAdmin):
list_display = ("id", "nome", "preco", "estoque", "criado_em", "atualizado_em")
search_fields = ("nome",)
list_filter = ("criado_em", "atualizado_em")list_display: colunas exibidas na listagem.search_fields: campos pesquisáveis.list_filter: filtros laterais.
5. Testando no Admin
Criar um superusuário:
python manage.py createsuperuserRodar o servidor:
python manage.py runserverAcessar: http://127.0.0.1:8000/admin/
Fazer login e cadastrar alguns produtos.
6. Exercícios práticos
- Criar 3 produtos de teste no admin.
- Adicionar um campo
categoria(CharFieldcom até 50 caracteres). - Refazer as migrações.
- Verificar se o campo aparece no admin.
Encerramento:
Nesta aula, aprendemos a criar uma Model, aplicar migrações e gerenciar dados no admin.
Na próxima aula (Aula 9), vamos expor este modelo como uma API REST usando o Django REST Framework, comparando com FastAPI/Express.
Objetivo:
- Apresentar o Django REST Framework (DRF).
- Criar a primeira API REST para o modelo
Produto - Explorar endpoints básicos (listar, detalhar, criar, atualizar e deletar).
- Testar a API no navegador, Postman e terminal.
1. O que é o Django REST Framework
O Django REST Framework (DRF) é uma biblioteca poderosa e flexível para criar APIs em Django. Ele fornece serializadores, views genéricas, roteadores, autenticação, permissões e paginação integradas. É o padrão de fato para APIs com Django.
Instalação:
pip install djangorestframework
pip freeze > requirements.txtConfiguração em settings.py:
INSTALLED_APPS = [
...,
'rest_framework',
'produtos',
]2. Criando o Serializer
Arquivo: produtos/serializers.py
from rest_framework import serializers
from .models import Produto
class ProdutoSerializer(serializers.ModelSerializer):
class Meta:
model = Produto
fields = '__all__'3. Criando as Views
Arquivo: produtos/views.py
from rest_framework import viewsets
from .models import Produto
from .serializers import ProdutoSerializer
class ProdutoViewSet(viewsets.ModelViewSet):
queryset = Produto.objects.all()
serializer_class = ProdutoSerializer4. Criando as rotas
Arquivo: config/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from produtos.views import ProdutoViewSet
# Criando o router
router = DefaultRouter()
router.register(r'produtos', ProdutoViewSet)
# Definindo as rotas
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
]Rodar o servidor:
python manage.py runserverAcessar no navegador:
- http://127.0.0.1:8000/api/produtos/ → Listagem e criação
- http://127.0.0.1:8000/api/produtos/1/ → Detalhe, edição e exclusão
Exercícios práticos
- Criar 3 produtos usando a API.
- Listar todos os produtos e verificar no Admin se foram salvos.
- Atualizar o preço de um produto via API.
- Deletar um produto e confirmar no Admin.
Encerramento:
Nesta aula, aprendemos a expor um modelo Django como API REST usando o DRF, com endpoints automáticos para CRUD. Na próxima aula, veremos como customizar a API com campos extras, filtros, buscas e paginação.
Objetivo da Aula:
Ensinar como habilitar a documentação automática da API no Django usando drf-spectacular, permitindo acesso via Swagger UI e Redoc.
Revisão rápida:
- No FastAPI, a documentação já vem pronta em
/docs. - No Express, usamos pacotes externos como
swagger-ui-express. - No Django REST Framework, a documentação precisa ser configurada, mas oferece opções avançadas.
Conteúdo
1. Introdução ao drf-spectacular
- O drf-spectacular é uma biblioteca moderna recomendada para gerar documentação OpenAPI no Django REST Framework.
- Permite criar documentação interativa (Swagger UI, Redoc) e exportar o schema OpenAPI em JSON.
2. Instalação e configuração
Instalar o pacote:
pip install drf-spectacularAdicionar ao INSTALLED_APPS no settings.py:
INSTALLED_APPS = [
...,
"rest_framework",
"drf_spectacular",
]Configurar o DRF para usar o schema do drf-spectacular:
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}3. Configuração das rotas
No arquivo urls.py principal do projeto:
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
SpectacularRedocView,
)
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("produtos.urls")),
# Rota para gerar o schema (JSON do OpenAPI)
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
# Swagger UI
path("api/docs/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
# Redoc
path("api/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]4. Testando a documentação
Rodar o servidor:
python manage.py runserverAcessar no navegador:
/api/schema/→ retorna o JSON da especificação OpenAPI./api/docs/→ abre o Swagger UI./api/redoc/→ abre o Redoc.
Os endpoints da API aparecem automaticamente na documentação.
5. Personalizando a documentação
Adicionar no settings.py:
SPECTACULAR_SETTINGS = {
"TITLE": "API de Produtos",
"DESCRIPTION": "Documentação da API de Produtos feita em Django REST Framework",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
}- O título, descrição e versão aparecem no Swagger e Redoc.
- É possível documentar endpoints e serializers com docstrings.
6. Exercício prático
- Acessar
/api/docs/e/api/redoc/. - Alterar o título e descrição da API no
settings.pye verificar a mudança. - Adicionar uma nova view (ex: categorias
/api/categorias/) e conferir se aparece na documentação.
Conclusão
- No Django REST Framework, a documentação precisa ser configurada, mas é poderosa e detalhada.
- Comparar com FastAPI (documentação automática) e Express (pacotes externos).
- O drf-spectacular facilita a geração de documentação interativa e padronizada.
Objetivo:
- Introduzir recursos de customização do Django REST Framework (DRF).
- Implementar filtros por campo, busca textual e ordenação em uma API.
- Testar as customizações no navegador e no Postman.
1. Revisão rápida
Na Aula 9, criamos a primeira API CRUD com DRF para o modelo Produto, utilizando endpoints automáticos para listagem, detalhe, criação, atualização e exclusão. Serializers e ViewSets simplificam a implementação.
Pergunta de reflexão: Como tornar a API mais útil quando temos muitos produtos?
2. Configurando filtros, busca e ordenação
O DRF possui recursos nativos para filtros simples, mas para filtros avançados podemos usar o pacote django-filter.
Instalação:
pip install django-filterConfiguração em settings.py:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
}3. Adicionando filtros e busca no ViewSet
Arquivo: produtos/views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.viewsets import ModelViewSet
from .models import Produto
from .serializers import ProdutoSerializer
class ProdutoViewSet(ModelViewSet):
queryset = Produto.objects.all()
serializer_class = ProdutoSerializer
# Filtros
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['preco', 'estoque'] # Filtragem exata por preço e estoque
search_fields = ['nome'] # Busca textual
ordering_fields = ['nome', 'preco'] # Ordenação
ordering = ['id'] # Ordenação padrão4. Testando a API
Rodar o servidor:
python manage.py runserverTestar no navegador e Postman usando parâmetros de consulta:
- Filtro por preço:
http://127.0.0.1:8000/api/produtos/?preco=150 - Busca por nome:
http://127.0.0.1:8000/api/produtos/?search=Mouse - Ordenação por preço decrescente:
http://127.0.0.1:8000/api/produtos/?ordering=-preco
Testar diferentes combinações de filtros, busca e ordenação.
Exercícios práticos
- Filtrar produtos por preço e estoque.
- Fazer busca textual pelo nome do produto.
- Ordenar produtos por preço crescente e decrescente.
- Testar diferentes combinações no navegador e no Postman.
Encerramento:
Nesta aula, aprendemos a customizar a API com filtros, busca e ordenação usando DRF e django-filter, tornando a API mais flexível e útil para o consumidor. Na próxima aula, veremos como adicionar paginação e campos extras.
Objetivo
Demonstrar como criar filtros avançados personalizados para a API com django-filter, incluindo filtros para faixa de preço (preco_minimo, preco_maximo) e filtros múltiplos para estoque sem a necessidade de filtros customizados.
Conteúdo
1. Filtros personalizados para faixa de preço
Crie (ou edite) o arquivo filters.py na app produtos:
from django_filters import FilterSet, NumberFilter
from .models import Produto
class ProdutoFilter(FilterSet):
preco_minimo = NumberFilter(field_name='preco', lookup_expr='gte')
preco_maximo = NumberFilter(field_name='preco', lookup_expr='lte')
preco = NumberFilter(field_name='preco', lookup_expr='exact')
class Meta:
model = Produto
fields = ['preco_minimo', 'preco_maximo', 'estoque']- Explicação:
preco_minimofiltra produtos com preço maior ou igual a um valor.preco_maximofiltra produtos com preço menor ou igual a um valor.precofiltra produtos com preço exatamente igual a um valor.estoquepode ser filtrado diretamente.
2. Filtros múltiplos para estoque sem filtros personalizados
No views.py, na sua ViewSet do Produto, você pode especificar no atributo filterset_fields que deseja permitir múltiplas operações para estoque, assim:
filterset_fields = {
'estoque': ['exact', 'gte', 'lte'], # permite filtro exato, maior ou igual e menor ou igual
}Dessa forma, sem precisar criar filtros customizados para estoque, a API suportará URLs do tipo:
?estoque=10(estoque exatamente 10)?estoque__gte=5(estoque maior ou igual a 5)?estoque__lte=20(estoque menor ou igual a 20)
3. Ajuste na ViewSet para integrar os filtros
Exemplo completo da ViewSet com filtros customizados e filtros múltiplos (sabendo que você pode usar filterset_class ou filterset_fields, mas não ambos ao mesmo tempo):
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import SearchFilter, OrderingFilter
from .models import Produto
from .serializers import ProdutoSerializer
from .filters import ProdutoFilter
class ProdutoViewSet(ModelViewSet):
queryset = Produto.objects.all()
serializer_class = ProdutoSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = ProdutoFilter # Ou usa filterset_fields
filterset_fields = {
'estoque': ['exact', 'gte', 'lte']
} # ou usa filterset_class
search_fields = ['nome']
ordering_fields = ['nome', 'preco']
ordering = ['id']4. Testando os filtros
Exemplos de URLs para testar no navegador ou Postman:
/api/produtos/?preco_minimo=100&preco_maximo=500/api/produtos/?estoque__gte=10&estoque__lte=50/api/produtos/?search=Mouse(busca textual pelo nome)/api/produtos/?ordering=-preco(ordenação por preço decrescente)/api/produtos/?preco_minimo=100&estoque__gte=10&search=Notebook&ordering=nome
5. Exercícios sugeridos
- Filtrar produtos por faixa de preço usando
preco_minimoepreco_maximo. - Filtrar produtos com estoque entre valores mínimo e máximo usando os operadores
gteelte. - Combinar filtros de preço, estoque, busca textual e ordenação.
- Testar as respostas da API nestes diferentes cenários para entender o comportamento.
Objetivo
Implementar paginação personalizada para APIs com Django REST Framework, adaptando o formato da resposta para incluir página atual, tamanho da página, total de páginas e resultados.
Conteúdo
1. Configuração global no settings.py
No arquivo settings.py, configure a paginação global para usar sua classe personalizada:
REST_FRAMEWORK = {
# Outras configurações do DRF...
'DEFAULT_PAGINATION_CLASS': 'config.pagination.CustomPagination', # ajuste o caminho conforme seu app
'PAGE_SIZE': 10, # quantidade padrão de itens por página
}2. Criando a páginação personalizada (arquivo pagination.py no seu projeto)
Crie o arquivo pagination.py no seu projeto e defina a classe CustomPagination:
from rest_framework import pagination
from rest_framework.response import Response
class CustomPagination(pagination.PageNumberPagination):
def get_paginated_response(self, data):
return Response({
'page': self.page.number,
'page_size': self.page.paginator.per_page,
'total_pages': self.page.paginator.num_pages,
'results': data,
})- Esta classe herda
PageNumberPagination. - Sobrescreve o método
get_paginated_responsepara modificar o formato padrão da resposta. - Os campos na resposta JSON serão:
page: página atual.page_size: tamanho da página.total_pages: total de páginas disponíveis.results: lista dos itens da página atual.
3. Testando a paginação
- A paginação será aplicada automaticamente em todas as views que retornem listas pois foi configurada globalmente.
- Exemplos de URLs para obter as páginas:
GET /api/produtos/?page=1
GET /api/produtos/?page=2
- A resposta será algo assim:
{
"page": 1,
"page_size": 10,
"total_pages": 5,
"results": [
// lista de produtos da página 1
]
}4. Exercícios práticos
- Faça requisições para diferentes páginas e observe como os resultados mudam.
- Altere o valor padrão
PAGE_SIZEe teste o comportamento da API. - Analise como a paginação interage com filtros e ordenação já implementados.
Objetivos
- Entender os diferentes níveis de validação que podem ser feitos em serializers.
- Implementar validações específicas em campos individuais (
validate_<field_name>). - Criar validações que envolvem múltiplos campos ao mesmo tempo (validação a nível do objeto).
- Personalizar mensagens de erro e garantir que a API retorne respostas claras a clientes.
Conteúdo
1. Revisão rápida dos serializers
- Serializers transformam dados entre formatos JSON e objetos Python.
- Além dessa conversão, serializers fazem validação automática baseada no modelo.
- Para regras específicas, é possível criar métodos de validação customizados.
2. Validação em campos individuais
Exemplo com validação para o campo preco, que precisa ser sempre maior que zero:
from rest_framework.serializers import ModelSerializer, ValidationError
from .models import Produto
class ProdutoSerializer(ModelSerializer):
class Meta:
model = Produto
fields = '__all__'
def validate_preco(self, preco):
if preco <= 0:
raise ValidationError('O preço deve ser maior que zero.')
return preco- O método deve ser nomeado como
validate_<nome_do_campo>. - Recebe o valor do campo e deve retornar o valor validado ou lançar um erro.
3. Validação cruzada entre campos (validação ao nível do objeto)
Para validar múltiplos campos em conjunto, use o método validate:
def validate(self, produto):
preco = produto.get('preco')
estoque = produto.get('estoque')
if estoque > 0 and (preco is None or preco <= 0):
raise ValidationError('Produto com estoque deve ter preço maior que zero.')
return produto- Recebe um dicionário com todos os campos validados até o momento.
- Deve retornar o dicionário de dados ou lançar
serializers.ValidationError.
4. Mensagens de erro personalizadas
- As mensagens lançadas por
ValidationErroraparecem no corpo da resposta com status HTTP 400. - Exemplo de retorno JSON em caso de erro:
{
"preco": [
"O preço deve ser maior que zero."
]
}- Isso ajuda o cliente a entender o que corrigir.
5. Exercícios práticos
- Adicione validação para o campo
nomepara garantir que não seja vazio. - Valide que o campo
estoquenão possa ser negativo. - Teste inserções inválidas via Postman ou browser e interprete as mensagens de erro da API.