Paginación y filtrado en Django REST Framework

Avanzado
Django
Django
Actualizado: 19/04/2026

Paginación en DRF

PageNumberPagination

La paginación más familiar para la mayoría de APIs:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
}

# Paginación personalizada
from rest_framework.pagination import PageNumberPagination

class PaginacionProductos(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'tamaño'   # ?tamaño=50
    max_page_size = 100
    page_query_param = 'pagina'        # ?pagina=2

    def get_paginated_response(self, data):
        return Response({
            'total': self.page.paginator.count,
            'paginas': self.page.paginator.num_pages,
            'pagina_actual': self.page.number,
            'siguiente': self.get_next_link(),
            'anterior': self.get_previous_link(),
            'resultados': data
        })

Diagrama conceptual de Paginación y filtrado en Django REST Framework

LimitOffsetPagination

Más flexible para APIs que necesitan saltar a una posición arbitraria:

from rest_framework.pagination import LimitOffsetPagination

class PaginacionLimitOffset(LimitOffsetPagination):
    default_limit = 20
    max_limit = 100
    limit_query_param = 'limite'     # ?limite=10&desplazamiento=20
    offset_query_param = 'desplazamiento'

CursorPagination

Ideal para feeds en tiempo real donde los datos cambian constantemente:

from rest_framework.pagination import CursorPagination

class PaginacionCursor(CursorPagination):
    page_size = 20
    ordering = '-fecha_creacion'  # Debe ser un campo único y ordenado
    cursor_query_param = 'cursor'

Filtrado con django-filter

pip install django-filter
# settings.py
INSTALLED_APPS += ['django_filters']

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
}

Filtrado básico en el ViewSet:

from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import Producto
from .serializers import ProductoSerializer

class ProductoViewSet(viewsets.ModelViewSet):
    queryset = Producto.objects.all()
    serializer_class = ProductoSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['categoria', 'activo', 'proveedor']
    # Uso: /api/productos/?categoria=1&activo=true

FilterSet personalizado

Para lógica de filtrado más compleja:

import django_filters
from .models import Producto

class FiltroProducto(django_filters.FilterSet):
    precio_min = django_filters.NumberFilter(field_name='precio', lookup_expr='gte')
    precio_max = django_filters.NumberFilter(field_name='precio', lookup_expr='lte')
    nombre = django_filters.CharFilter(lookup_expr='icontains')
    creado_despues = django_filters.DateFilter(field_name='fecha_creacion', lookup_expr='gte')
    creado_antes = django_filters.DateFilter(field_name='fecha_creacion', lookup_expr='lte')
    categoria_nombre = django_filters.CharFilter(
        field_name='categoria__nombre',
        lookup_expr='icontains'
    )

    class Meta:
        model = Producto
        fields = ['activo', 'proveedor', 'precio_min', 'precio_max']

class ProductoViewSet(viewsets.ModelViewSet):
    queryset = Producto.objects.select_related('categoria', 'proveedor')
    serializer_class = ProductoSerializer
    filterset_class = FiltroProducto
    # Uso: /api/productos/?precio_min=10&precio_max=100&nombre=laptop

SearchFilter

Búsqueda de texto en múltiples campos:

from rest_framework.filters import SearchFilter, OrderingFilter

class ProductoViewSet(viewsets.ModelViewSet):
    queryset = Producto.objects.all()
    serializer_class = ProductoSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = FiltroProducto
    search_fields = [
        'nombre',
        'descripcion',
        '=categoria__slug',       # Búsqueda exacta (=)
        '^proveedor__nombre',     # Empieza por (^)
        '@descripcion',           # Búsqueda full-text (@) - PostgreSQL
    ]
    ordering_fields = ['precio', 'nombre', 'fecha_creacion', 'stock']
    ordering = ['-fecha_creacion']   # Orden por defecto

    # Uso búsqueda: /api/productos/?search=laptop
    # Uso ordenación: /api/productos/?ordering=-precio,nombre

Combinar filtros, búsqueda y paginación

class ArticuloViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Articulo.objects.filter(publicado=True).select_related('autor', 'categoria')
    serializer_class = ArticuloSerializer
    pagination_class = PaginacionProductos
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['categoria', 'autor', 'destacado']
    search_fields = ['titulo', 'resumen', 'contenido']
    ordering_fields = ['fecha_publicacion', 'titulo', 'visitas']
    ordering = ['-fecha_publicacion']

    # Ejemplo de URL compleja:
    # /api/articulos/?categoria=1&search=django&ordering=-visitas&pagina=2&tamaño=10

Throttling (limitación de tasa)

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
    }
}

# Throttling por vista
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle

class BusquedaViewSet(viewsets.ReadOnlyModelViewSet):
    throttle_classes = [AnonRateThrottle, UserRateThrottle]
    # ...

La combinación de paginación, filtrado con django-filter, búsqueda con SearchFilter y ordenación con OrderingFilter proporciona una API REST flexible y eficiente sin necesidad de escribir casi ningún código a mano.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Configurar paginación con PageNumberPagination, LimitOffsetPagination y CursorPagination. Usar django-filter para filtrar por campos del modelo en la API. Aplicar SearchFilter para búsqueda de texto en múltiples campos. Implementar OrderingFilter para ordenación de resultados en la API. Crear filtros personalizados con FilterSet para lógica de filtrado compleja.

Cursos que incluyen esta lección

Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje