Skip to main content

Paginador dinámico avanzado para Django REST Framework con optimizaciones automáticas

Project description

Django Dynamic Paginator

Un paginador dinámico y altamente optimizado para Django REST Framework que elimina consultas N+1, optimiza JOINs automáticamente y proporciona filtrado avanzado con campos dinámicos controlados desde query parameters.

Características principales

  • Optimización automática de consultas: Detecta y combina filtros de la misma tabla relacionada evitando dobles JOINs
  • Campos dinámicos desde query params: Control total sobre campos SQL y serializer desde la URL
  • Filtros dinámicos inteligentes: Soporte para filtros base, exclusiones y Q objects complejos
  • Búsqueda multi-campo: Búsqueda eficiente en múltiples campos con Q objects optimizados
  • Mapeo automático de ForeignKeys: Convierte automáticamente user a user_id según sea necesario
  • Paginación opcional: Soporte para resultados ilimitados via query parameter
  • Ordenamiento avanzado: Manejo inteligente de campos NULL y validación automática
  • Filtros de fecha: Rango de fechas dinámico con campos personalizables
  • Serializers dinámicos: Integración completa con DynamicFieldsModelSerializer

Instalación

pip install django-dynamic-paginator

Configuración rápida

from django_dynamic_paginator import SimpleDynamicPaginatorService
from rest_framework.views import APIView

class ProductListView(APIView):
    def get(self, request):
        paginator = SimpleDynamicPaginatorService(
            model=Product,
            serializer_class=ProductDynamicSerializer,
            search_fields=['name', 'description'],
            allowed_filters=['category', 'status', 'price_range'],
            select_related=['category', 'brand'],
            enable_dynamic_fields=True  # ✨ Campos dinámicos habilitados
        )
        return paginator.handle_request(request, account_by=request.user.account)

🚀 Nuevas características: Campos dinámicos

Control total desde query parameters

# Solo campos específicos (optimiza SQL + Serializer)
GET /api/products/?only_fields=id,name,price

# Excluir campos innecesarios
GET /api/products/?exclude_fields=created_at,updated_at

# Campos anidados personalizados
GET /api/products/?nested_fields={"category":{"only_fields":["id","name"]}}

# Combinación de filtros y campos
GET /api/products/?only_fields=id,name,category&status=active&search=laptop

Serializer dinámico requerido

from django_dynamic_paginator.serializers import DynamicFieldsModelSerializer

class ProductDynamicSerializer(DynamicFieldsModelSerializer):
    category_name = serializers.SerializerMethodField()
    
    def get_category_name(self, obj):
        return obj.category.name if obj.category else None
    
    class Meta:
        model = Product
        exclude = ['account_by', 'internal_notes']  # Excluir campos sensibles
        # O usar fields explícitos:
        # fields = ['id', 'name', 'price', 'category', 'category_name', 'status']

Ejemplos de uso avanzado

Módulos con diferentes necesidades de campos

# Vista base del paginador (sin only_fields fijo)
class ProductListView(APIView):
    def get(self, request):
        paginator = SimpleDynamicPaginatorService(
            model=Product,
            serializer_class=ProductDynamicSerializer,
            search_fields=['name', 'description'],
            select_related=['category', 'brand'],
            enable_dynamic_fields=True,
            allow_unlimited=True
        )
        return paginator.handle_request(request, account_by=request.user.account)
# Módulo Manager básico - Solo datos esenciales
GET /api/products/?only_fields=id,name,price
# SQL: SELECT id, name, price FROM product...

# Módulo Dashboard completo - Todos los datos
GET /api/products/?only_fields=id,name,price,category,brand,status,created_at
# SQL: SELECT id, name, price, category_id, brand_id, status, created_at FROM product...

# Módulo Reportes - Sin campos pesados
GET /api/products/?exclude_fields=description,images,metadata

Filtros relacionados optimizados

# ANTES: Genera dobles JOINs innecesarios
# SELECT ... FROM product 
# INNER JOIN category c1 ON ... 
# INNER JOIN category c2 ON ... 
# WHERE c1.type = 'electronics' AND c2.status = 'active'

# DESPUÉS: Un solo JOIN optimizado
paginator.handle_request(request,
    category__type='electronics',
    category__status='active'  # Se combina automáticamente
)

Q objects complejos

from django.db.models import Q

# Filtros complejos con lógica OR/AND
complex_filter = (
    Q(created_by=request.user.id) | 
    Q(assigned_to=request.user.id) |
    Q(collaborators__user=request.user.id)
)

paginator.handle_request(request, _q_filter=complex_filter)

Exclusiones automáticas

# Excluir registros automáticamente
paginator.handle_request(request,
    status='active',
    exclude_category_id=5,  # Excluye automáticamente category_id=5
    exclude_deleted=True    # Excluye deleted=True
)

Parámetros de query automáticos

El paginador acepta automáticamente estos parámetros via URL:

# Paginación
GET /api/products/?page=2

# 🆕 Campos dinámicos
GET /api/products/?only_fields=id,name,price
GET /api/products/?exclude_fields=created_at,updated_at
GET /api/products/?nested_fields={"category":{"only_fields":["id","name"]}}

# Búsqueda multi-campo
GET /api/products/?search=laptop

# Filtros dinámicos (según allowed_filters)
GET /api/products/?category=electronics&status=active

# Ordenamiento
GET /api/products/?sortBy=price&sortDesc=true

# Filtros de fecha
GET /api/products/?startDate=2024-01-01&endDate=2024-12-31&field_date=created_at

# Filtros múltiples
GET /api/products/?category_in=1,2,3&status_in=active,pending

# Sin paginación (si allow_unlimited=True)
GET /api/products/?unlimited=true

Configuración completa

paginator = SimpleDynamicPaginatorService(
    model=Product,                          # Modelo Django
    serializer_class=ProductDynamicSerializer, # Serializer dinámico DRF
    search_fields=['name', 'description'],  # Campos de búsqueda
    page_size=25,                          # Elementos por página
    allowed_filters=[                       # Filtros permitidos via URL
        'category', 'status', 'brand',
        'category__type', 'brand__country'  # Filtros relacionados
    ],
    select_related=[                        # Optimización JOINs
        'category', 'brand', 'supplier'
    ],
    prefetch_related=[                      # Optimización M2M
        'tags', 'reviews__user'
    ],
    only_fields=[                          # 🆕 Campos fallback (opcional)
        'id', 'name', 'price', 'category'  # Se usa solo si no hay query params
    ],
    allow_unlimited=True,                  # Permitir ?unlimited=true
    enable_dynamic_fields=True             # 🆕 Habilitar campos dinámicos
)

Performance

Antes vs Después

# ❌ ANTES: Consulta ineficiente
products = Product.objects.filter(
    category__type='electronics'
).filter(
    category__status='active'    # Doble JOIN innecesario
)
# SQL: 2 JOINs + múltiples queries N+1

# ✅ DESPUÉS: Consulta optimizada  
paginator.handle_request(request,
    category__type='electronics',
    category__status='active'
)
# + Query params: ?only_fields=id,name,price
# SQL: 1 JOIN + select_related automático + only() campos específicos

Optimización por módulos

# Módulo lista rápida - Solo 3 campos
GET /api/products/?only_fields=id,name,price
# SQL: SELECT id, name, price FROM product LIMIT 25
# Transferencia: ~500 bytes por registro

# Módulo detalle completo - Todos los campos necesarios  
GET /api/products/?exclude_fields=internal_data,bulk_metadata
# SQL: SELECT * FROM product EXCEPT internal_data, bulk_metadata
# Transferencia: Solo datos útiles para el frontend

Resultados reales

  • Reducción de queries: 70-90% menos consultas SQL
  • Tiempo de respuesta: Mejora de 500ms a 50ms en datasets grandes
  • Memoria: 60% menos uso de memoria con only_fields dinámicos
  • Transferencia de red: 40-80% menos datos transferidos según módulo

Precedencia de configuración

  1. Query parameters (máxima prioridad)

    • ?only_fields=id,name → Controla SQL + Serializer
    • ?exclude_fields=created_at → Solo afecta Serializer
  2. Constructor (fallback)

    • only_fields=['id', 'name'] → Se usa si no hay query params
  3. Sin configuración

    • SQL: SELECT * (menos eficiente pero funcional)

Compatibilidad

  • Python 3.8+
  • Django 3.2+
  • Django REST Framework 3.12+

Contribuir

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/nueva-funcionalidad)
  3. Commit tus cambios (git commit -am 'Agrega nueva funcionalidad')
  4. Push a la rama (git push origin feature/nueva-funcionalidad)
  5. Crea un Pull Request

Licencia

MIT License - ver archivo LICENSE para detalles.

Changelog

v1.3.1 🚀

Correcciones de Estabilidad y Profundidad

Soporte de Expansión Recursiva Multinivel

  • Fix: expand_nested_to_only_fields ahora es plenamente recursivo.
  • Problema: Las relaciones de tercer nivel (ej: ProductWarehouse -> Product -> Measurement) causaban $N+1$ porque el only() no alcanzaba a "ver" los campos del nieto.
  • Solución: Implementación de un sistema de prefix acumulativo que construye rutas SQL profundas como product__measurement__ms_description.
  • Resultado: Los Joins profundos ahora traen sus datos en la consulta principal del repositorio.

Blindaje contra Relaciones Virtuales (Inversas)

  • Fix: Validación de integridad física antes de inyectar sufijos _id.
  • Error resuelto: Requisition has no field named 'validations_id'.
  • Causa: El sistema intentaba pedir una columna física _id para relaciones inversas (donde la llave está en la otra tabla).
  • Solución: Implementación de un check de many_to_one / one_to_one. El sistema ahora distingue entre una FK Física (donde agrega el ID) y una Relación Virtual (donde deja que el Prefetch actúe solo).

Preservación de Atributos de Unión (Pegamento)

  • Mejora: Uso de field.attname en lugar de construcción manual de strings para detectar llaves foráneas.
  • Beneficio: Soporte nativo para nombres de columna personalizados (ej: si la FK se llama po_ref en lugar de purchase_order, el sistema la encuentra y la preserva automáticamente).

Métricas de v1.3.1

  • Profundidad de Optimización: Ilimitada (soporta N niveles de nested_fields).
  • Compatibilidad de Modelos: Soporte total para ForeignKeys directas y RelatedManager (inversas) simultáneamente.
  • SQL Final: Limpieza total de errores por "DeferredAttribute" en objetos anidados.

v1.3.0 🆕

Correcciones Críticas

Optimización de FKs en Prefetch y Select Related

  • Fix: Eliminado auto-agregado indiscriminado de TODOS los FKs del modelo en queries anidadas
  • Antes: Se agregaban automáticamente category_id, measurement_id, client_by_id, etc. aunque no se solicitaran
  • Ahora: Solo se agregan FKs estrictamente necesarios:
    • FK del modelo padre (ej: purchase_order_id en items/validations)
    • FKs de relaciones explícitas en select_related
  • Impacto: Reducción significativa de columnas en queries SQL, menos transferencia de datos

Detección Dinámica del FK Padre en Prefetch

  • Fix: El FK del modelo padre ahora se detecta dinámicamente en lugar de asumir el formato
  • Problema resuelto: purchaseorder_id vs purchase_order_id - Django genera nombres con guión bajo
  • Solución: Búsqueda automática del campo FK real que apunta al parent_model
  • Resultado: Eliminación de micro-queries por deferred field access

Validación de Campos _id en Only()

  • Fix: validate_sql_only_fields ahora acepta campos FK con formato relation_id
  • Antes: purchase_order_id se descartaba porque solo purchase_order estaba en model_field_names
  • Ahora: Valida que si un campo termina en _id, la relación base exista en el modelo
  • Impacto: Preservación correcta de todos los FKs necesarios en queries optimizadas

📊 Mejoras de Performance

  • Reducción de queries: De 7 queries a 3 queries en endpoints complejos (-57%)
  • Eliminación de micro-queries: CERO queries adicionales del tipo SELECT ... WHERE id = X LIMIT 21
  • Queries más limpias: Solo columnas solicitadas explícitamente + FKs necesarios
  • Control preciso: El desarrollador tiene control exacto sobre qué campos se cargan

🔍 Debug Mejorado

  • Logs detallados de FKs agregados automáticamente
  • Trazabilidad completa del flujo INPUT → OUTPUT en validate_sql_only_fields
  • Identificación clara de campos preservados y descartados

✅ Beneficios

  • Backward compatible: No requiere cambios en código existente
  • Automático: No es necesario especificar manualmente FKs padre en only_fields
  • Robusto: Funciona independientemente del naming convention de Django
  • Eficiente: Menos datos transferidos, queries más rápidas

v1.2.9 🆕

  • Mejoras en metodo get_by_id: Se mejora el metodo get_by_id para que pueda aplicar el only_fields dinámico

v1.2.8 🆕

  • Correcion en select_related: Se corrige el error de que no se aplicaba el only_fields dinámico al select_related

v1.2.7 🆕

  • Correcion en select_related: Se agrega el modo estricto para el select_related en get_by_id

v1.2.6 🆕

  • Correcion en select_related: Se corrige el error de que no se aplicaba el only_fields dinámico al select_related

v1.2.5 🆕

  • Correcion en select_related: Se corrige el error de que no se aplicaba el only_fields dinámico al select_related

v1.2.4 🆕

  • Nueva funcionalidad: Se agrega soporte para nested_fields recursivos de cualquier profundidad

v1.2.3 🆕

  • Nueva funcionalidad: Cambios en la documentación

v1.2.2 🆕

  • Se agrega soporte para Annotations: Se agrega soporte para annotations en el paginador dinámico

v1.2.1 🆕

  • Correcion en prefetch_related: Se corrige el error de que no se aplicaba el only_fields dinámico al prefetch_related

v1.2.0 🆕

  • Nueva función: Se añade soporte para operadores de comparación en los filtros dinámicos

v1.1.8 🆕

  • Correcion en prefetch_related: Se corrige el error de que no se aplicaba el only_fields dinámico al prefetch_related

v1.1.7 🆕

  • Correcion en serializer: Se corrige el error de que no se aplicaba el only_fields dinámico al serializer

v1.1.6 🆕

  • Refactorización de código: Movidos métodos auxiliares a utils.py para mejor organización
  • Nueva función: Añadido auto_detect_exact_match_fields para detección automática de campos de coincidencia exacta
  • Mejoras en la arquitectura: Mejorada la modularidad y reutilización del código
  • Documentación: Agregadas documentaciones detalladas para las nuevas funciones
  • Mantenimiento: Actualizadas las dependencias y corregidas advertencias de tipado

v1.1.5 🆕

  • Versión corregida: Actualización de versión a 1.1.5

v1.1.4 🆕

  • Soporte para nested_fields en prefetch_related: Optimización automática de queries con Prefetch y only() para relaciones Many-to-Many y reverse ForeignKey
  • Optimización SQL en nested serializers: Los campos especificados en nested_fields ahora se aplican directamente a las consultas SQL de prefetch, reduciendo significativamente los datos transferidos
  • Validación automática de campos anidados: Valida que los campos especificados en nested_fields existan en el modelo relacionado antes de aplicar only()
  • Mejoras en logging de debug: Información detallada sobre campos aplicados en prefetch optimizado
  • Reducción de payload: Ejemplo: de 14 campos a 8 campos en queries de items (-43% de datos)
  • Compatibilidad con modelos complejos: Soporte para relaciones anidadas y prefetcheos profundos
  • Soporte para prefetcheos profundos: Manejo correcto de relaciones anidadas como category__parent__grandparent
  • Soporte para relaciones anidadas: Optimización automática de queries con Prefetch y only() para relaciones profundas
  • Mejoras en manejo de prefetcheos anidados: Soporte completo para relaciones múltiples en prefetch_related
  • Mejoras en validación de modelos: Verificación más robusta de modelos y relaciones en nested_fields
  • Mejoras en manejo de errores: Mensajes de error más descriptivos para problemas de configuración
  • Mejoras en compatibilidad: Soporte mejorado para modelos con relaciones complejas y anidadas
  • Mejoras en rendimiento: Optimizaciones adicionales en la construcción de queries SQL
  • Mejoras en robustez: Manejo más seguro de casos extremos y configuraciones complejas

v1.1.3 🆕

  • Corrección de versión: Actualización de versión para publicación correcta
  • Manejo de relaciones dinámicas en select_related: Soporte para relaciones profundas como category__parent__grandparent
  • Mejoras en optimización de consultas: Mayor eficiencia en la generación de SQL con relaciones anidadas

v1.1.2 🆕

  • Corrección de errores: Se corrigieron errores de sintaxis y lógica en la implementación de los campos dinámicos.

v1.1.1 🆕

  • Corrección de errores: Se corrigieron errores de sintaxis y lógica en la implementación de los campos dinámicos.

v1.1.0 🆕

  • Campos dinámicos desde query params: Control total sobre SQL y serializer
  • Precedencia query params > constructor: Los parámetros URL tienen prioridad máxima
  • Validación automática de campos SQL: Convierte campos relacionados automáticamente
  • Serializer dinámico integrado: Soporte completo para DynamicFieldsModelSerializer
  • Respuesta limpia: Removido campo dynamic_fields de la respuesta JSON

v1.0.0

  • Lanzamiento inicial
  • Soporte para filtros dinámicos
  • Optimización automática de JOINs
  • Búsqueda multi-campo
  • Mapeo automático de ForeignKeys

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_dynamic_paginator-1.3.1.tar.gz (30.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_dynamic_paginator-1.3.1-py3-none-any.whl (24.2 kB view details)

Uploaded Python 3

File details

Details for the file django_dynamic_paginator-1.3.1.tar.gz.

File metadata

  • Download URL: django_dynamic_paginator-1.3.1.tar.gz
  • Upload date:
  • Size: 30.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for django_dynamic_paginator-1.3.1.tar.gz
Algorithm Hash digest
SHA256 936c0d2955a220fbb43b510da643ee4011e421dfea82ff61385bd37f532db31b
MD5 52dee1622334301bab1333f1b155fe36
BLAKE2b-256 c542f350ee1ee6939b2413c13f6b794795f3c736b9455f90984960d0e6651100

See more details on using hashes here.

File details

Details for the file django_dynamic_paginator-1.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_dynamic_paginator-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3cb54bda95ef3f37817fbebd82c2c69ce12feea9036acbf7a36d4e9f16dcb243
MD5 385ff4df4c839b9eb6dc740172dd3ca1
BLAKE2b-256 7b7a4fe90e1c11ac4cb375a70d567304b84c18903f569672653a7380ea6b71f0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page