Paginador integrado#

Este es el artículo número 24 de la serie de tutoriales de Django Ninja.
La función de paginación (pagination) es de considerable importancia, incluso en proyectos pequeños con volúmenes de datos reducidos.
Sin paginación, la API seguirá funcionando, solo que el rendimiento se verá afectado, especialmente cuando el volumen de datos sea grande.
Cuando la API devuelve una gran cantidad de datos a la vez, no solo aumenta la carga del servidor, sino que también puede ralentizar el procesamiento del cliente o provocar problemas como tiempos de espera agotados (timeout) o memoria insuficiente.
A través de la paginación, podemos evitar transferir grandes cantidades de datos de una sola vez, mejorando el rendimiento de la API y elevando la experiencia del usuario.
Este tema se dividirá en dos partes (superior e inferior) para presentar cómo implementar la función de paginación en Django Ninja: desde el paginador integrado hasta clases de paginación personalizadas, con el fin de satisfacer diferentes necesidades.
Todos los cambios de código de este artículo se pueden consultar en este PR.
Proyecto de ejemplo en GitHub#
La importancia de la paginación#
El papel fundamental de la paginación es dividir grandes volúmenes de datos en pequeñas partes para su transmisión, enviando solo un poco cada vez, evitando así problemas de rendimiento.
Específicamente, la paginación nos ayuda a:
- Reducir la presión del servidor: No es necesario devolver todos los datos a la vez, solo procesar los datos de una sola página.
- Mejorar la velocidad de transmisión de red: Transferir cantidades excesivas de datos aumenta la latencia de red y el riesgo de pérdida de datos; mediante la paginación se reduce eficazmente la demanda de transmisión.
- Mejorar la experiencia del usuario: El cliente puede obtener y mostrar datos iniciales rápidamente sin tener que esperar a que se completen todas las transferencias de datos. Al mismo tiempo, la paginación reduce la presión sobre el cliente para procesar grandes volúmenes de datos.
Por lo tanto, independientemente del tamaño del proyecto, implementar una estrategia de paginación eficiente es de gran ayuda para la escalabilidad de la API y la experiencia del usuario.
Una vez comprendida la importancia de la paginación, ¡comencemos a implementar!
La API de ejemplo de esta entrega es: «Obtener la lista de artículos».
Dado que la base de datos del proyecto ya cuenta con más de 60 registros de artículos, es ideal para esta demostración.
¿Qué? ¿Dices que no los tienes? Te invitamos a consultar la sección «Descanso y preparación» al final de la «Entrega 12: Solicitudes (Parte 4) Introducción a Request Body y Schema».
En este artículo utilizaremos el paginador integrado PageNumberPagination de Django Ninja para implementar una función de paginación simple y efectiva. La parte personalizada la dejaremos para el siguiente artículo.
Paginador integrado de Django Ninja#
En Django Ninja, la función de paginación se puede implementar mediante el decorador integrado paginate junto con un paginador (es decir, una clase de paginación).
Django Ninja proporciona dos paginadores integrados; para facilitar su comprensión, aquí tienes una explicación sencilla de cada uno:
LimitOffsetPagination: Pagina en función de «desde qué registro comenzar» y «cuántos registros obtener», adecuado para volúmenes de datos muy grandes. Por ejemplo: «Comenzar desde el registro 20 y obtener 10 registros».PageNumberPagination: Pagina a través del número de página; el usuario solo necesita especificar el número de página deseado, como «obtener los datos de la página 2». La cantidad de datos por página puede ser configurada por el desarrollador.
Personalmente prefiero usar PageNumberPagination o personalizar una versión similar (que es el contenido del siguiente artículo).
Sin embargo, el paginador por defecto es LimitOffsetPagination, por lo que al usar el decorador paginate, debes declarar explícitamente el primer argumento. Ya lo verás en un momento.
Antes de eso, repasemos el estado actual de la API «Obtener la lista de artículos».
Estado actual de la API#
Como se muestra a continuación, al no tener implementada la función de paginación, devolverá todos los datos de los artículos de una sola vez:
@router.get('/posts/', response=list[PostListResponse], ...)
def get_posts(
request: HttpRequest,
title: None | str = Query(None, min_length=2, max_length=10),
) -> QuerySet[Post]:
"""
Obtener la lista de artículos
"""
posts = Post.objects.all()
if title:
posts = posts.filter(
title__icontains=title).select_related('author')
return posts
Esto funciona con normalidad cuando hay pocos artículos, pero a medida que aumente el volumen de datos, el rendimiento se verá afectado.
A continuación, mejoraremos este problema mediante el paginador integrado de Django Ninja.
Implementar paginación con PageNumberPagination#
Usando el paginador integrado PageNumberPagination, podemos agregar fácilmente la función de paginación a la API.
Solo necesitas usar el decorador @paginate en la función view y agregar los parámetros, así:
from ninja.pagination import PageNumberPagination, paginate
...
@router.get(...)
@paginate(PageNumberPagination, page_size=10) # Implementación de paginación
def get_posts(...) -> QuerySet[Post]:
"""
Obtener la lista de artículos
"""
...
He omitido la mayor parte del contenido; aquí solo nos enfocaremos en la implementación de la paginación.
Como se mencionó anteriormente, debes declarar explícitamente el primer argumento PageNumberPagination (si se usara LimitOffsetPagination, no sería necesario).
El efecto de la implementación es que cada página mostrará 10 artículos, cantidad que se puede controlar mediante el parámetro page_size.
Oye, ¿y qué pasa con el cambio de página? Echemos un vistazo al código fuente de PageNumberPagination:
class PageNumberPagination(AsyncPaginationBase):
class Input(Schema):
page: int = Field(1, ge=1)
...
Input representa los parámetros de consulta de la solicitud (se detallará en el próximo artículo). En otras palabras, puedes usar el parámetro page en los parámetros de consulta (query parameters) de la URL para especificar el «número de página» y lograr el cambio de página.
Probar el efecto de paginación#
Llama a la API utilizando ?page=2 como parámetro de consulta para ver el resultado:

¡Tal como se esperaba!
Lo que se muestra en la respuesta es efectivamente el contenido de la página 2: los IDs de los artículos comienzan desde el 11, con un total de 10 registros.
Ventajas y limitaciones de usar el paginador integrado#
Ventajas#
- Basta con usar un simple decorador de paginación + paginador integrado para lograr la paginación, además de poder controlar la cantidad por página, lo cual es muy conveniente y práctico.
- Adecuado para situaciones que no requieren personalizaciones complejas.
Limitaciones#
- Falta de flexibilidad; por ejemplo, no podemos permitir que el usuario especifique cuántos datos mostrar por página.
- Los campos y el formato de la respuesta son fijos (y un poco escuetos).
Resumen#
A través del paginador integrado de Django Ninja, podemos incorporar rápidamente la función de paginación a nuestras APIs, respondiendo de inmediato a necesidades sencillas de paginación.
Sin embargo, el paginador integrado adolece de flexibilidad de control y tampoco permite crear respuestas personalizadas. Cuando las necesidades de paginación se vuelven complejas, se queda un poco corto.
En tales casos, un paginador personalizado será una mejor solución.
En el próximo artículo exploraremos cómo personalizar clases de paginación en Django Ninja para satisfacer estas necesidades avanzadas.