Sistema de gerenciamento de biblioteca, desenvolvido como projeto de estudo migrando de uma aplicação CLI em Python puro para uma API REST completa em Django REST Framework, com autenticação JWT, perfis de acesso e um front-end simples em HTML/CSS/JavaScript para demonstração.
Projeto em evolução contínua — parte de um roteiro de aprendizado que passou por: CLI Python (DAO/View) → Django → PostgreSQL → Django REST Framework → autenticação JWT → refatoração (segurança, testes, Docker).
- CRUD de livros, autores, categorias, editoras, exemplares e usuários
- Registro de empréstimos, com validação de duplicidade e disponibilidade
- Registro de devoluções, com data de devolução e atualização automática do exemplar
- Regra de negócio transacional: se qualquer passo falhar, nada é gravado pela metade
- Autenticação via JWT (tokens de acesso e renovação)
- Perfis de acesso: qualquer usuário logado consulta o catálogo, mas só bibliotecários alteram dados, registram empréstimos/devoluções e gerenciam usuários
- Senhas guardadas com hash (nunca aparecem na API) e configuração sensível em
.env - Histórico preservado: não é possível apagar um usuário ou exemplar que já tenha empréstimos
- Front-end mínimo para visualizar o catálogo e testar o fluxo de empréstimo/devolução
- Backend: Python 3.14, Django 6.1, Django REST Framework
- Banco de dados: PostgreSQL
- Autenticação: JWT (
djangorestframework-simplejwt) - Infra: Docker, Docker Compose e deploy no Render (
gunicorn+whitenoise) - Front-end: HTML, CSS e JavaScript puro (sem frameworks)
- Outras libs:
django-cors-headers,psycopg2-binary,python-decouple
biblioteca_django/
├── biblioteca/ # App Django
│ ├── models.py # Modelos (Livro, Exemplar, Emprestimo, Usuario...)
│ ├── services.py # Regra de negócio de empréstimo e devolução
│ ├── permissions.py # IsBibliotecario
│ ├── serializers.py
│ ├── views.py
│ ├── urls.py
│ └── tests.py
├── bibliotech_django/ # Configuração do projeto (settings, urls raiz)
├── frontend/ # Front-end estático (HTML/CSS/JS)
├── Dockerfile
├── render.yaml # Blueprint de deploy (Render)
├── build.sh # Build da API no Render
├── docker-compose.yml
├── .env.example # Modelo das variáveis de ambiente
├── manage.py
└── requirements.txt
É o caminho mais curto: só precisa de Docker com o plugin Compose e do Git.
git clone https://github.com/pdrocairo/biblioteca_django.git
cd biblioteca_django
cp .env.example .envAbra o .env e preencha SECRET_KEY e DB_PASSWORD. Para gerar uma chave:
python3 -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
⚠️ Mantenha aSECRET_KEYentre aspas simples, como no exemplo: ela pode conter$, que o Docker Compose interpretaria como variável.
Os valores de DB_HOST e DB_PORT do .env valem só para rodar sem Docker;
o Compose já aponta o Django para o serviço db por conta própria.
docker compose up --buildIsso sobe o PostgreSQL e o Django, e aplica as migrations automaticamente.
A API fica em http://127.0.0.1:8000/api/.
Em outro terminal:
docker compose exec web python manage.py createsuperuserTodo superusuário já nasce com perfil de bibliotecário.
O front-end é estático e fica fora do Compose. Em outro terminal, na raiz do projeto:
python3 -m http.server 5500 --directory frontendAcesse http://127.0.0.1:5500 e faça login com o usuário do passo 3.
| Comando | O que faz |
|---|---|
docker compose up -d |
Sobe em segundo plano |
docker compose logs -f web |
Acompanha os logs do Django |
docker compose exec web python manage.py test |
Roda os testes |
docker compose down |
Para os serviços (mantém os dados) |
docker compose down -v |
Para e apaga o banco do container |
O PostgreSQL do container fica exposto na porta 5433 do seu computador, para
não colidir com um Postgres local na 5432. Dá para conectar com qualquer cliente
(DBeaver, psql) usando o banco, usuário e senha do .env.
- Python 3.12+
- PostgreSQL rodando localmente
- Git
git clone https://github.com/pdrocairo/biblioteca_django.git
cd biblioteca_django
python3 -m venv .venv
source .venv/bin/activate # Linux / WSL / macOS
# .venv\Scripts\activate # Windows (PowerShell)
pip install -r requirements.txtsudo -u postgres psqlDentro do psql:
CREATE DATABASE bibliotech_db;
ALTER USER postgres PASSWORD 'sua_senha_aqui';
\qcp .env.example .envPreencha o .env com a SECRET_KEY e os dados do banco criado no passo anterior
(DB_NAME, DB_USER, DB_PASSWORD, DB_HOST=localhost, DB_PORT=5432).
python3 manage.py migrate
python3 manage.py createsuperuser
python3 manage.py runserverA API estará em http://127.0.0.1:8000/api/. Para o front-end, siga o passo 4
da seção do Docker.
⚠️ O front-end espera a API emhttp://127.0.0.1:8000. Se mudar a porta do Django, ajuste o endereço emfrontend/config.js.
A suíte cobre empréstimo, bloqueio por empréstimo ativo, livro sem exemplar, devolução (incluindo exemplar que não está emprestado), acesso sem token (401), permissões por perfil (403), escrita restrita ao bibliotecário, ausência de senha nas respostas, login JWT e a proteção do histórico de empréstimos.
python3 manage.py test # sem Docker
docker compose exec web python manage.py test # com DockerA API usa JWT. Para obter um token:
POST /api/token/
Content-Type: application/json
{
"username": "seu_usuario",
"password": "sua_senha"
}
A resposta traz dois tokens:
{
"refresh": "...",
"access": "..."
}Use o access no header das requisições seguintes:
Authorization: Bearer SEU_ACCESS_TOKEN
Quando o access expirar, renove sem precisar logar de novo:
POST /api/token/refresh/
Content-Type: application/json
{
"refresh": "SEU_REFRESH_TOKEN"
}
Cada Usuario está ligado a um User do Django (um único cadastro) e tem um
perfil:
| Perfil | Pode |
|---|---|
leitor |
Apenas consultar o catálogo (livros, autores, categorias, editoras, exemplares) |
bibliotecario |
Tudo do leitor, mais cadastrar/alterar/remover itens do catálogo, ver e registrar empréstimos/devoluções e gerenciar usuários |
Um superusuário criado com createsuperuser já é bibliotecário. Os demais
usuários são cadastrados por um bibliotecário em POST /api/usuarios/.
Todos exigem o header Authorization: Bearer <token>, exceto os de /api/token/.
| Método | Endpoint | Descrição | Quem |
|---|---|---|---|
| POST | /api/token/ |
Login (gera os tokens JWT) | público |
| POST | /api/token/refresh/ |
Renova o token de acesso | público |
| GET | /api/livros/, /api/autores/, /api/categorias/, /api/editoras/, /api/exemplares/ |
Lista (e detalha em /{id}/) |
autenticado |
| POST, PUT, PATCH, DELETE | os mesmos endpoints | Cadastra / altera / remove | bibliotecário |
| GET | /api/emprestimos/ e /api/emprestimos/{id}/ |
Consulta os empréstimos (somente leitura) | bibliotecário |
| POST | /api/emprestimos/registrar_emprestimo/ |
Registra um empréstimo | bibliotecário |
| PATCH | /api/emprestimos/registrar_devolucoes/ |
Registra uma devolução | bibliotecário |
| GET, POST | /api/usuarios/ |
Lista / cadastra usuários | bibliotecário |
| GET, PUT, PATCH, DELETE | /api/usuarios/{id}/ |
Detalha / altera / remove um usuário | bibliotecário |
Empréstimos não podem ser criados nem alterados pela rota genérica (POST /api/emprestimos/
responde 405): eles só nascem e terminam pelas duas ações acima, que passam pela regra de negócio.
POST /api/emprestimos/registrar_emprestimo/
{ "id_usuario": 2, "id_livro": 1 }
PATCH /api/emprestimos/registrar_devolucoes/
{ "id_usuario": 2, "id_exemplar": 1 }
| Status | Quando |
|---|---|
201 / 200 |
Empréstimo / devolução registrados |
400 |
Campos ausentes ou inválidos (ids precisam ser inteiros positivos) |
401 |
Sem token ou token expirado |
403 |
Usuário autenticado que não é bibliotecário |
404 |
Usuário, livro ou exemplar inexistente |
409 |
Usuário já tem empréstimo ativo, livro sem exemplar disponível, ou devolução de um exemplar que o usuário não tem emprestado |
Erros de regra de negócio vêm como {"erro": "mensagem"}.
POST /api/usuarios/
{
"nome": "Maria Silva",
"email": "maria@exemplo.com",
"perfil": "leitor",
"username": "maria",
"password": "uma-senha-forte-123"
}
A senha é validada pelos validadores do Django e guardada com hash. Ela nunca é devolvida pela API.
O arquivo render.yaml descreve tudo (API, front-end estático e
PostgreSQL), então o deploy é um "Blueprint":
-
Suba o projeto para o GitHub e crie uma conta em render.com.
-
No painel: New → Blueprint, escolha este repositório e confirme.
-
Quando o Render pedir as variáveis marcadas para preenchimento manual, informe:
Variável Serviço Valor DJANGO_SUPERUSER_USERNAMEbibliotech-apilogin do primeiro bibliotecário DJANGO_SUPERUSER_EMAILbibliotech-apie-mail dele DJANGO_SUPERUSER_PASSWORDbibliotech-apiuma senha forte (nesse modo o Django não valida a senha, então escolha uma boa) CORS_ALLOWED_ORIGINSbibliotech-apiURL do front-end, ex.: https://bibliotech-web.onrender.comAPI_URLbibliotech-webURL da API, ex.: https://bibliotech-api.onrender.com -
Aguarde o build. A
SECRET_KEYé gerada pelo Render, e aDATABASE_URLvem do banco.
Se o nome
bibliotech-apioubibliotech-webjá estiver em uso, o Render acrescenta um sufixo à URL. Nesse caso, corrijaCORS_ALLOWED_ORIGINSeAPI_URLcom as URLs reais (em Environment) e faça um novo deploy dos dois serviços.
O primeiro bibliotecário é criado na subida da API com as variáveis
DJANGO_SUPERUSER_*, porque o plano gratuito não oferece Shell. Depois, ele
cadastra os demais usuários em POST /api/usuarios/.
Limites do plano gratuito (confira as condições atuais no site do Render): a API "dorme" após alguns minutos sem uso e a primeira requisição depois disso demora cerca de um minuto; o PostgreSQL gratuito expira depois de 30 dias. Serve para portfólio, mas não para dados que você precise manter.
- Documentação automática da API (Swagger com
drf-spectacular) - Usar
gunicorntambém na imagem Docker (hoje ela usa orunserver, só para desenvolvimento) - Migrar o front-end para React
Dívida conhecida:
Usuarioainda repetenomeeUserdo Django já guarda.