{"id":30453484,"url":"https://github.com/luccagiankolenez/portfolio-django-api","last_synced_at":"2026-05-08T07:31:11.815Z","repository":{"id":310740198,"uuid":"1040985299","full_name":"LuccaGianKolenez/portfolio-django-api","owner":"LuccaGianKolenez","description":"Backend em Django + DRF + Celery + PostgreSQL + Redis + JWT.","archived":false,"fork":false,"pushed_at":"2025-08-19T22:59:50.000Z","size":10,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-20T00:29:56.388Z","etag":null,"topics":["backend","celery","django","drf","jwt","postgresql","python","redis"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LuccaGianKolenez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-19T20:02:20.000Z","updated_at":"2025-08-19T22:59:54.000Z","dependencies_parsed_at":"2025-08-20T00:29:58.063Z","dependency_job_id":"bf062b35-cbdd-4205-beef-8f0f7a824981","html_url":"https://github.com/LuccaGianKolenez/portfolio-django-api","commit_stats":null,"previous_names":["luccagiankolenez/portfolio-django-api"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/LuccaGianKolenez/portfolio-django-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuccaGianKolenez%2Fportfolio-django-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuccaGianKolenez%2Fportfolio-django-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuccaGianKolenez%2Fportfolio-django-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuccaGianKolenez%2Fportfolio-django-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LuccaGianKolenez","download_url":"https://codeload.github.com/LuccaGianKolenez/portfolio-django-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuccaGianKolenez%2Fportfolio-django-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271755342,"owners_count":24815381,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","celery","django","drf","jwt","postgresql","python","redis"],"created_at":"2025-08-23T16:00:46.415Z","updated_at":"2026-05-08T07:31:11.781Z","avatar_url":"https://github.com/LuccaGianKolenez.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Portfolio — Django API (DRF + JWT + Swagger)\n\nAPI de portfólio construída com **Django REST Framework**, autenticação **JWT (SimpleJWT)** e documentação **OpenAPI** via **drf-spectacular**. Projeto preparado para testes com **pytest** e qualidade de código com **ruff/black/isort/mypy**.\n\n\u003e Objetivo: servir como referência **sênior** de estrutura, organização e boas práticas para APIs em Django/DRF.\n\n---\n\n## Sumário\n- [Arquitetura \u0026 Stack](#arquitetura--stack)\n- [Estrutura de Pastas](#estrutura-de-pastas)\n- [Variáveis de Ambiente](#variáveis-de-ambiente)\n- [Como Rodar (Local)](#como-rodar-local)\n- [Rotas Principais](#rotas-principais)\n- [Autenticação (JWT)](#autenticação-jwt)\n- [Domínio: Items (CRUD)](#domínio-items-crud)\n- [Postman Collection](#postman-collection)\n- [Testes \u0026 Qualidade](#testes--qualidade)\n- [Erros Comuns](#erros-comuns)\n- [Roadmap](#roadmap)\n- [Licença](#licença)\n\n---\n\n## Arquitetura \u0026 Stack\n\n- **Django 4+/5+** + **Django REST Framework**\n- **JWT** com `djangorestframework-simplejwt`\n- **OpenAPI/Swagger/ReDoc** com `drf-spectacular` (+ `drf-spectacular-sidecar`)\n- **Filtros/Busca/Ordenação**: `django-filter`, `search_fields`, `ordering_fields`\n- **Config 12-factor** via **.env** (`django-environ`)\n- Qualidade: **pytest**, **ruff**, **black**, **isort**, **mypy**\n\n---\n\n## Estrutura de Pastas\n\n```\nportfolio-django-api/\n├─ config/\n│  ├─ settings/\n│  │  ├─ base.py        # DRF, JWT, Spectacular, filtros, etc.\n│  │  ├─ dev.py         # DEBUG=True\n│  │  └─ prod.py        # DEBUG=False\n│  ├─ urls.py           # roteamento principal (API/Swagger/ReDoc/JWT)\n│  └─ wsgi.py\n├─ core/\n│  ├─ views.py          # /api/health/\n│  └─ tests.py          # teste do health\n├─ items/\n│  ├─ models.py         # Item (UUID, name, price, created_at)\n│  ├─ serializers.py\n│  └─ views.py          # ViewSet com filtros, busca e ordenação\n├─ postman/\n│  └─ portfolio-django-api.postman_collection.json\n├─ .env.example\n├─ pyproject.toml       # pytest, ruff, black, isort, mypy\n├─ manage.py\n└─ .gitignore\n```\n\n---\n\n## Variáveis de Ambiente\n\nArquivo: **`.env`** (baseado em `.env.example`)\n\n```env\nDJANGO_SECRET_KEY=change-me\nDJANGO_DEBUG=true\nDJANGO_ALLOWED_HOSTS=127.0.0.1,localhost\nDATABASE_URL=sqlite:///db.sqlite3\n```\n\n\u003e Para produção, ajuste `DJANGO_DEBUG=false`, configure `DJANGO_ALLOWED_HOSTS` e use um banco gerenciado (Postgres etc.) via `DATABASE_URL`.\n\n---\n\n## Como Rodar (Local)\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\n\npython -m pip install -U pip setuptools wheel\npip install django djangorestframework django-filter \\\n            drf-spectacular drf-spectacular-sidecar \\\n            djangorestframework-simplejwt django-environ \\\n            pytest pytest-django ruff black isort mypy types-requests\n\ncp .env.example .env\npython manage.py makemigrations\npython manage.py migrate\n\n# (opcional) usuário admin para uso no Django Admin ou testes JWT\npython manage.py createsuperuser\n\npython manage.py runserver\n```\n\n- Swagger UI: **http://127.0.0.1:8000/api/docs/**\n- OpenAPI (JSON): **http://127.0.0.1:8000/api/schema/**\n- ReDoc: **http://127.0.0.1:8000/api/redoc/**\n- Health: **http://127.0.0.1:8000/api/health/**\n\n---\n\n## Rotas Principais\n\n| Recurso           | Método(s)                     | Caminho                         | Auth           |\n|-------------------|-------------------------------|----------------------------------|----------------|\n| **Health**        | `GET`                          | `/api/health/`                  | Público        |\n| **OpenAPI JSON**  | `GET`                          | `/api/schema/`                  | Público        |\n| **Swagger UI**    | `GET`                          | `/api/docs/`                    | Público        |\n| **ReDoc**         | `GET`                          | `/api/redoc/`                   | Público        |\n| **JWT Obtain**    | `POST`                         | `/api/token/`                   | Público        |\n| **JWT Refresh**   | `POST`                         | `/api/token/refresh/`           | Público        |\n| **Items**         | `GET \\| POST`                  | `/api/items/`                   | **JWT**        |\n| **Item por ID**   | `GET \\| PUT \\| PATCH \\| DELETE`| `/api/items/{uuid}/`            | **JWT**        |\n\n**Health (exemplo)**\n```bash\ncurl -s http://127.0.0.1:8000/api/health/\n# -\u003e {\"status\":\"ok\"}\n```\n\n---\n\n## Autenticação (JWT)\n\n### Obter tokens (`/api/token/`)\n```bash\ncurl -s -X POST http://127.0.0.1:8000/api/token/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"seuusuario\",\"password\":\"suasenha\"}'\n```\n**Resposta**\n```json\n{ \"refresh\": \"...\", \"access\": \"...\" }\n```\n\n### Renovar access (`/api/token/refresh/`)\n```bash\ncurl -s -X POST http://127.0.0.1:8000/api/token/refresh/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"refresh\":\"\u003ctoken-de-refresh\u003e\"}'\n```\n\n\u003e Configure o header `Authorization: Bearer \u003caccess\u003e` em chamadas autenticadas.\n\n---\n\n## Domínio: Items (CRUD)\n\n**Modelo**: `id (UUID, PK)`, `name (str)`, `price (decimal)`, `created_at (auto_now_add)`\n\n**Listar** (paginação + busca + ordenação)\n```bash\ncurl -s \"http://127.0.0.1:8000/api/items/?search=note\u0026ordering=-created_at\" \\\n  -H \"Authorization: Bearer \u003caccess\u003e\"\n```\nParâmetros suportados:\n- `search`: busca em `name`\n- `ordering`: `name`, `price`, `created_at` (use `-` para desc)\n\n**Criar**\n```bash\ncurl -s -X POST http://127.0.0.1:8000/api/items/ \\\n  -H \"Authorization: Bearer \u003caccess\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Notebook\",\"price\":\"1999.90\"}'\n```\n\n**Detalhar / Atualizar / Patch / Remover**\n```bash\n# GET\ncurl -s http://127.0.0.1:8000/api/items/\u003cuuid\u003e/ -H \"Authorization: Bearer \u003caccess\u003e\"\n\n# PUT (replace)\ncurl -s -X PUT http://127.0.0.1:8000/api/items/\u003cuuid\u003e/ \\\n  -H \"Authorization: Bearer \u003caccess\u003e\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Notebook Pro\",\"price\":\"2999.90\"}'\n\n# PATCH (parcial)\ncurl -s -X PATCH http://127.0.0.1:8000/api/items/\u003cuuid\u003e/ \\\n  -H \"Authorization: Bearer \u003caccess\u003e\" -H \"Content-Type: application/json\" \\\n  -d '{\"price\":\"2799.90\"}'\n\n# DELETE\ncurl -s -X DELETE http://127.0.0.1:8000/api/items/\u003cuuid\u003e/ \\\n  -H \"Authorization: Bearer \u003caccess\u003e\"\n```\n\n---\n\n## Postman Collection\n\nA coleção com todos os endpoints e **scripts de teste** para salvar `{{token}}`/`{{refresh}}` automaticamente está em:\n```\npostman/portfolio-django-api.postman_collection.json\n```\nPassos:\n1. Importar no Postman\n2. Executar **JWT • Obtain** (preencher variáveis `token` e `refresh`)\n3. Usar as requisições de **Items** com `Authorization: Bearer {{token}}`\n\n---\n\n## Testes \u0026 Qualidade\n\n**Testes**\n```bash\npytest -q\n```\n**Linters e formatação**\n```bash\nruff .\nblack --check .\nisort --check-only .\n# Para formatar:\nblack . \u0026\u0026 isort .\n```\n\n\u003e Teste de exemplo: `core/tests.py` valida o **/api/health/**. Expanda com testes de domínio (items) e autenticação.\n\n---\n\n## Erros Comuns\n\n- **401 Unauthorized**: faltou `Authorization: Bearer \u003caccess\u003e` ou token expirado → gere/renove.\n- **403 Forbidden**: usuário sem permissão para a ação.\n- **400 Bad Request**: payload inválido; verifique tipos, requireds e formato (`Content-Type: application/json`).\n- **404 Not Found**: ID inexistente ou rota incorreta.\n- **Swagger 404**: confirme `drf_spectacular`/`sidecar` em `INSTALLED_APPS` e rotas `/api/schema`/`/api/docs`/`/api/redoc` no `urls.py`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluccagiankolenez%2Fportfolio-django-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluccagiankolenez%2Fportfolio-django-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluccagiankolenez%2Fportfolio-django-api/lists"}