API dinâmica para Django que gera endpoints RESTful automaticamente para qualquer modelo
Project description
API Dinâmica Django
Uma API RESTful dinâmica baseada em Django Rest Framework que permite operações CRUD em qualquer modelo Django sem a necessidade de criar viewsets específicos para cada modelo.
⚠️ Beta: Este pacote está em fase beta. Use em ambiente de produção por sua conta e risco.
Instalação
pip install drf-builder
Sumário
- Visão Geral
- Estrutura do Projeto
- Endpoints
- Autenticação
- Operações HTTP
- Recursos Avançados
- Tratamento de Erros
- Exemplos de Uso
- Documentação da API
Visão Geral
A API Dinâmica é uma solução para interação com modelos Django de forma genérica e flexível. Ela foi projetada para:
- Criar endpoints RESTful para qualquer modelo Django sem código adicional
- Suportar todas as operações CRUD básicas (Create, Read, Update, Delete)
- Permitir filtragem dinâmica de resultados
- Facilitar operações com relacionamentos aninhados
- Fornecer uma estrutura consistente para tratamento de erros
- Permitir seleção de campos específicos para retorno
- Oferecer controle de profundidade para serialização de relacionamentos
- Suportar operações em lote e exclusão em cascata
Estrutura do Projeto
A API é composta pelos seguintes componentes principais:
- DynamicModelViewSet: ViewSet central que gerencia todas as operações CRUD
- DynamicSerializer: Serializer que se adapta dinamicamente a qualquer modelo
- Middleware de Exceções: Sistema para tratamento padronizado de erros
- Utilitários: Funções auxiliares para operações comuns
api/
├── viewsets.py # Contém DynamicModelViewSet
├── serializers.py # Contém DynamicSerializer
├── exceptions.py # Define exceções personalizadas
├── middleware.py # Implementa tratamento padronizado de erros
├── utils.py # Funções de utilidade
└── routers.py # Configuração de rotas
Endpoints
A API segue um padrão de URL consistente:
- Lista/Criação:
/api/v1/<app>/<model>/ - Recuperar/Atualizar/Excluir:
/api/v1/<app>/<model>/<pk>/
Onde:
<app>é o nome do aplicativo Django<model>é o nome do modelo<pk>é o ID do registro
Autenticação
A API usa autenticação JWT (JSON Web Token) através do pacote rest_framework_simplejwt. Os seguintes endpoints estão disponíveis para autenticação:
- Obter Token:
POST /api/v1/token/ - Atualizar Token:
POST /api/v1/token/refresh/ - Verificar Token:
POST /api/v1/token/verify/
Exemplo de obtenção de token:
POST /api/v1/token/
{
"username": "usuario",
"password": "senha"
}
Resposta de exemplo:
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
Para autenticar solicitações subsequentes, inclua o token de acesso no cabeçalho HTTP:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Operações HTTP
GET (Listar)
Retorna uma lista de todos os objetos de um modelo específico.
Endpoint:
GET /api/v1/<app>/<model>/
Parâmetros de consulta:
depth: Nível de profundidade para serializar relacionamentos (0-10)filter_<campo>: Filtrar resultados por campo específicogroup_by: Agrupar resultados por um ou mais camposfields: Seleção de campos específicos para retornar, separados por vírgula
Exemplo de requisição:
GET /api/v1/blog/post/?filter_status=published&depth=1
Resposta de exemplo:
[
{
"id": 1,
"title": "Primeiro Post",
"content": "Conteúdo do post...",
"status": "published",
"author": {
"id": 1,
"name": "João Silva"
},
"_meta": {
"model": "post",
"app": "blog"
}
},
{
"id": 2,
"title": "Segundo Post",
"content": "Mais conteúdo...",
"status": "published",
"author": {
"id": 2,
"name": "Maria Souza"
},
"_meta": {
"model": "post",
"app": "blog"
}
}
]
GET (Recuperar)
Retorna um objeto específico pelo seu ID.
Endpoint:
GET /api/v1/<app>/<model>/<pk>/
Parâmetros de consulta:
depth: Nível de profundidade para serializar relacionamentos (0-10)fields: Seleção de campos específicos para retornar, separados por vírgula
Exemplo de requisição:
GET /api/v1/blog/post/1/?depth=2
Resposta de exemplo:
{
"id": 1,
"title": "Primeiro Post",
"content": "Conteúdo do post...",
"status": "published",
"author": {
"id": 1,
"name": "João Silva",
"email": "joao@exemplo.com"
},
"comment_set": [
{
"id": 1,
"text": "Ótimo post!",
"created_at": "2023-01-15T14:30:00Z"
},
{
"id": 2,
"text": "Muito interessante.",
"created_at": "2023-01-16T10:15:00Z"
}
],
"_meta": {
"model": "post",
"app": "blog"
}
}
POST (Criar)
Cria um novo objeto do modelo especificado.
Endpoint:
POST /api/v1/<app>/<model>/
Exemplo de requisição:
POST /api/v1/blog/post/
Payload de exemplo:
{
"title": "Novo Post",
"content": "Este é um novo post",
"status": "draft",
"author": 1,
"comment_set": [
{
"text": "Um comentário inicial",
"author_name": "Leitor Assíduo"
}
]
}
Resposta de exemplo:
{
"id": 3,
"title": "Novo Post",
"content": "Este é um novo post",
"status": "draft",
"author": 1,
"created_at": "2023-07-10T08:45:12Z",
"_meta": {
"model": "post",
"app": "blog"
}
}
PATCH (Atualizar)
Atualiza parcialmente um objeto existente.
Endpoint:
PATCH /api/v1/<app>/<model>/<pk>/
Exemplo de requisição:
PATCH /api/v1/blog/post/3/
Payload de exemplo:
{
"status": "published",
"comment_set": [
{
"id": 5,
"text": "Comentário atualizado"
},
{
"text": "Novo comentário"
}
]
}
Resposta de exemplo:
{
"id": 3,
"title": "Novo Post",
"content": "Este é um novo post",
"status": "published",
"author": 1,
"created_at": "2023-07-10T08:45:12Z",
"_meta": {
"model": "post",
"app": "blog"
}
}
PUT (Atualizar Completo)
Atualiza completamente um objeto existente, substituindo todos os campos.
Endpoint:
PUT /api/v1/<app>/<model>/<pk>/
Exemplo de requisição:
PUT /api/v1/blog/post/3/
Payload de exemplo:
{
"title": "Título Completamente Novo",
"content": "Conteúdo totalmente atualizado",
"status": "published",
"author": 1,
"comment_set": [
{
"id": 5,
"text": "Comentário atualizado"
},
{
"text": "Novo comentário"
}
]
}
Resposta de exemplo:
{
"id": 3,
"title": "Título Completamente Novo",
"content": "Conteúdo totalmente atualizado",
"status": "published",
"author": 1,
"created_at": "2023-07-10T08:45:12Z",
"updated_at": "2023-07-11T10:22:45Z",
"_meta": {
"model": "post",
"app": "blog"
}
}
Observação: Diferente do PATCH, o PUT requer que todos os campos obrigatórios sejam fornecidos, mesmo que não estejam sendo alterados.
DELETE (Excluir)
Remove um objeto específico pelo seu ID.
Endpoint:
DELETE /api/v1/<app>/<model>/<pk>/
Exemplo de requisição:
DELETE /api/v1/blog/post/3/
Payload opcional para exclusão em cascata:
{
"comment_set": [
{
"id": 5
},
{
"id": 6
}
]
}
Resposta: Status HTTP 204 No Content
Recursos Avançados
Filtragem
A API suporta filtragem dinâmica usando o prefixo filter_ seguido pelo nome do campo.
Exemplos:
Filtrar posts por autor:
GET /api/v1/blog/post/?filter_author=1
Filtrar posts por status:
GET /api/v1/blog/post/?filter_status=published
Combinando múltiplos filtros:
GET /api/v1/blog/post/?filter_status=published&filter_author=1
Agrupamento
O parâmetro group_by permite agrupar resultados por um ou mais campos, retornando contagens.
Exemplo:
GET /api/v1/blog/post/?group_by=status
Resposta de exemplo:
[
{
"status": "published",
"count": 15
},
{
"status": "draft",
"count": 7
}
]
Agrupamento por múltiplos campos:
GET /api/v1/blog/post/?group_by=status,author
Resposta de exemplo:
[
{
"status": "published",
"children": [
{
"author": 1,
"count": 8
},
{
"author": 2,
"count": 7
}
]
},
{
"status": "draft",
"children": [
{
"author": 1,
"count": 3
},
{
"author": 2,
"count": 4
}
]
}
]
Relacionamentos Aninhados
A API suporta a criação e atualização de objetos relacionados em uma única operação.
Exemplo de criação com objetos aninhados:
{
"title": "Post com comentários",
"content": "Conteúdo...",
"author": 1,
"comment_set": [
{
"text": "Primeiro comentário",
"author_name": "João"
},
{
"text": "Segundo comentário",
"author_name": "Maria"
}
]
}
Exemplo de atualização com objetos aninhados:
{
"title": "Título atualizado",
"comment_set": [
{
"id": 1,
"text": "Comentário atualizado"
},
{
"text": "Novo comentário"
}
]
}
Validação de Objetos Aninhados
A API valida automaticamente os objetos aninhados, garantindo que:
- Todos os campos obrigatórios estejam presentes
- Os tipos de dados estejam corretos
- Os objetos relacionados referenciados existam
- As permissões sejam respeitadas
Se alguma validação falhar, a operação inteira é revertida (transação atômica) e um erro detalhado é retornado, indicando qual objeto aninhado apresentou o problema.
Controle de Profundidade
O parâmetro depth controla quantos níveis de objetos relacionados são incluídos na resposta.
GET /api/v1/blog/post/1/?depth=0 # Apenas o objeto principal
GET /api/v1/blog/post/1/?depth=1 # Inclui objetos diretamente relacionados
GET /api/v1/blog/post/1/?depth=2 # Inclui também os objetos relacionados aos relacionados
Exemplo com depth=0:
{
"id": 1,
"title": "Primeiro Post",
"content": "Conteúdo...",
"author": 1
}
Exemplo com depth=1:
{
"id": 1,
"title": "Primeiro Post",
"content": "Conteúdo...",
"author": {
"id": 1,
"name": "João Silva",
"email": "joao@exemplo.com"
}
}
Limitações de Profundidade:
- A profundidade máxima permitida é 10 (para evitar problemas de desempenho)
- Valores negativos resultarão em erro
Seleção de Campos
O parâmetro fields permite selecionar apenas campos específicos para retornar na resposta.
GET /api/v1/blog/post/?fields=id,title,status # Retorna apenas id, título e status
GET /api/v1/blog/post/1/?fields=title,content,author # Retorna apenas título, conteúdo e autor
Exemplo com seleção de campos:
{
"id": 1,
"title": "Primeiro Post",
"status": "published",
"_meta": {
"model": "post",
"app": "blog",
"selected_fields": ["id", "title", "status"]
}
}
A seleção de campos também funciona em combinação com outros parâmetros:
GET /api/v1/blog/post/?fields=title,author&depth=1 # Campos específicos com relacionamentos
GET /api/v1/blog/post/?fields=title,status&group_by=status # Campos específicos com agrupamento
Observações:
- O campo
idé sempre incluído, mesmo se não for explicitamente solicitado - Se um campo solicitado não existir no modelo, ele será ignorado
- Para operações de agrupamento, os campos de agrupamento são automaticamente incluídos
Exclusão em Cascata
A API suporta exclusão em cascata controlada de objetos relacionados.
Exemplo de exclusão em cascata:
DELETE /api/v1/blog/post/1/
Payload para exclusão em cascata:
{
"comment_set": [
{
"id": 1
},
{
"id": 2
}
],
"tag_set": [] // Exclui todos os tags relacionados
}
Observações:
- Um array vazio (
[]) indica que todos os objetos relacionados devem ser excluídos - Um array com objetos contendo IDs especifica quais objetos relacionados devem ser excluídos
- Se nenhum payload for fornecido, apenas o objeto principal é excluído
Tratamento de Erros
A API implementa um sistema abrangente de tratamento de erros, fornecendo respostas consistentes e informativas quando ocorrem problemas.
Tipos de Exceções
| Exceção | Código HTTP | Descrição |
|---|---|---|
| ModelNotFoundError | 404 | Modelo especificado não encontrado |
| RelatedObjectNotFoundError | 404 | Objeto relacionado não encontrado |
| InvalidRelationshipError | 400 | Relacionamento inválido especificado |
| NestedObjectError | 400 | Erro ao processar objeto aninhado |
| InvalidFilterError | 400 | Filtro inválido especificado |
| InvalidDepthError | 400 | Valor de profundidade inválido |
| OperationError | 400 | Erro genérico durante a operação |
| ValidationError | 400 | Erro de validação nos dados fornecidos |
Exemplos de Mensagens de Erro
Modelo não encontrado:
{
"message": "Modelo 'article' não encontrado no app 'blog' nem no app 'core'.",
"type": "ModelNotFoundError"
}
Objeto relacionado não encontrado:
{
"message": "Não foi possível encontrar Author com ID 99 para o campo 'author'.",
"type": "RelatedObjectNotFoundError",
"details": {
"detail": "Não foi possível encontrar Author com ID 99 para o campo 'author'.",
"context": {
"model": "Author",
"id": "99",
"code": "related_object_not_found",
"field": "author"
}
}
}
Erro de validação:
{
"message": "Erro de validação: title: Este campo é obrigatório.",
"type": "ValidationError",
"details": {
"title": "Este campo é obrigatório."
},
"fields": {
"title": "Este campo é obrigatório."
}
}
Erro em objeto aninhado:
{
"comment_set": {
"message": "Campos obrigatórios faltando em comment: text",
"path": "comment_set[0]",
"context": {
"path": "comment_set[0]",
"relation_field": "comment_set",
"code": "nested_object_error",
"index": 0,
"missing_fields": ["text"]
}
}
}
Filtro inválido:
{
"filter": "Filtro inválido 'invalid_field': O campo não existe no modelo Post.",
"type": "InvalidFilterError"
}
Profundidade inválida:
{
"depth": "Valor de profundidade inválido 'xyz'. Deve ser um número inteiro válido.",
"type": "InvalidDepthError"
}
Exemplos de Uso
Exemplo 1: CRUD Básico
Criar um autor:
POST /api/v1/blog/author/
{
"name": "João Silva",
"email": "joao@exemplo.com",
"bio": "Escritor de tecnologia"
}
Resposta:
{
"id": 1,
"name": "João Silva",
"email": "joao@exemplo.com",
"bio": "Escritor de tecnologia",
"created_at": "2023-07-01T14:30:00Z",
"_meta": {
"model": "author",
"app": "blog"
}
}
Recuperar o autor:
GET /api/v1/blog/author/1/
Atualizar o autor:
PATCH /api/v1/blog/author/1/
{
"bio": "Escritor de tecnologia e entusiasta de IA"
}
Excluir o autor:
DELETE /api/v1/blog/author/1/
Exemplo 2: Relacionamentos
Criar um post com comentários:
POST /api/v1/blog/post/
{
"title": "API Dinâmica",
"content": "Como criar uma API dinâmica com Django",
"status": "published",
"author": 2,
"tag_set": [
{ "name": "django" },
{ "name": "api" },
{ "name": "python" }
],
"comment_set": [
{
"text": "Excelente tutorial!",
"author_name": "Leitor"
}
]
}
Atualizar um post e seus comentários:
PATCH /api/v1/blog/post/1/
{
"status": "featured",
"comment_set": [
{
"id": 1,
"text": "Comentário atualizado"
},
{
"text": "Novo comentário"
}
]
}
Exemplo 3: Filtragem e Agrupamento
Filtrar posts publicados:
GET /api/v1/blog/post/?filter_status=published
Agrupar posts por status:
GET /api/v1/blog/post/?group_by=status
Filtrar e agrupar:
GET /api/v1/blog/post/?filter_author=2&group_by=status
Relacionamentos profundos:
GET /api/v1/blog/post/1/?depth=2
Resposta:
{
"id": 1,
"title": "API Dinâmica",
"content": "Como criar uma API dinâmica com Django",
"status": "featured",
"author": {
"id": 2,
"name": "Maria Souza",
"email": "maria@exemplo.com",
"bio": "Desenvolvedora Python"
},
"tag_set": [
{
"id": 1,
"name": "django",
"post": 1
},
{
"id": 2,
"name": "api",
"post": 1
},
{
"id": 3,
"name": "python",
"post": 1
}
],
"comment_set": [
{
"id": 1,
"text": "Comentário atualizado",
"author_name": "Leitor",
"post": 1
},
{
"id": 2,
"text": "Novo comentário",
"post": 1
}
],
"_meta": {
"model": "post",
"app": "blog"
}
}
Exemplo 4: Seleção de Campos
Selecionar campos específicos:
GET /api/v1/blog/post/?fields=title,status,created_at
Resposta:
[
{
"id": 1,
"title": "API Dinâmica",
"status": "featured",
"created_at": "2023-07-01T14:30:00Z",
"_meta": {
"model": "post",
"app": "blog",
"selected_fields": ["id", "title", "status", "created_at"]
}
},
{
"id": 2,
"title": "Segundo Post",
"status": "published",
"created_at": "2023-07-05T09:15:00Z",
"_meta": {
"model": "post",
"app": "blog",
"selected_fields": ["id", "title", "status", "created_at"]
}
}
]
Combinar seleção de campos com profundidade:
GET /api/v1/blog/post/1/?fields=title,author,comment_set&depth=1
Resposta:
{
"id": 1,
"title": "API Dinâmica",
"author": {
"id": 2,
"name": "Maria Souza",
"email": "maria@exemplo.com"
},
"comment_set": [
{
"id": 1,
"text": "Comentário atualizado",
"author_name": "Leitor"
},
{
"id": 2,
"text": "Novo comentário"
}
],
"_meta": {
"model": "post",
"app": "blog",
"selected_fields": ["id", "title", "author", "comment_set"]
}
}
Selecionar campos em uma operação de agrupamento:
GET /api/v1/blog/post/?fields=status,author&group_by=status
Resposta:
[
{
"status": "published",
"count": 15
},
{
"status": "draft",
"count": 7
}
]
Note que ao usar seleção de campos com agrupamento, os campos de agrupamento têm prioridade e sempre serão incluídos na resposta.
Exemplo 5: Exclusão com Cascata
Excluir um post e alguns comentários específicos:
DELETE /api/v1/blog/post/1/
Payload:
{
"comment_set": [
{ "id": 1 },
{ "id": 3 }
]
}
Excluir um post e todos os seus comentários:
DELETE /api/v1/blog/post/2/
Payload:
{
"comment_set": []
}
Documentação da API
A API inclui documentação interativa através do Swagger UI e ReDoc. Se a documentação estiver habilitada nas configurações (API_DOCS_ENABLED = True), os seguintes endpoints estarão disponíveis:
- Swagger UI:
/api/v1/swagger/ - ReDoc:
/api/v1/redoc/ - Arquivo JSON da API:
/api/v1/docs.json - Arquivo YAML da API:
/api/v1/docs.yaml
A documentação interativa permite:
- Visualizar todos os endpoints disponíveis
- Testar chamadas diretamente da interface
- Ver modelos de dados e exemplos de requisição/resposta
- Verificar parâmetros e opções de cada endpoint
A documentação da API é atualizada automaticamente e reflete a estrutura atual da API, incluindo modelos dinâmicos e endpoints.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file drf_builder-0.1.0.tar.gz.
File metadata
- Download URL: drf_builder-0.1.0.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3814d2370fa5ed6855fa51ca7a97ded6ae6b38dd9457d33b0f78ca3cd5bd06ac
|
|
| MD5 |
9759d79b552161cca40f08960cdbdea9
|
|
| BLAKE2b-256 |
c539eda1c5878682aa050a50da0efadd96db71558e13a8fba7c36ba53772e789
|
File details
Details for the file drf_builder-0.1.0-py3-none-any.whl.
File metadata
- Download URL: drf_builder-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f207c0245b754d0e9dc94454e8a1be44d5101ba7af689cb613d0ff5f6256c12
|
|
| MD5 |
cefb3fa6d7dc86666e401a2b7a824656
|
|
| BLAKE2b-256 |
13ff784b25d9971a5ac2dbfdb5003865cdf3286aad6e81d365eb65748c4c21e3
|