El pipeline real de imágenes: se sube un original, un worker de Celery lo convierte a WebP en tres tamaños, y hoy se sirve directamente desde PostgreSQL (no hay S3, CDN ni servicio de archivos aparte todavía).
¿Qué es?
La app apps.fotos, con su propia base de datos (leon_fotos). Guarda las fotos de cada variante y sus tamaños ya procesados. No hay comprobantes en PDF ni documentos genéricos aquí — solo imágenes de producto.
Cómo funciona hoy
1. Subida:POST /imagenes (multipart) crea el registro imagen_producto y un primer render "original" con el archivo tal cual se subió.
2. Procesado en segundo plano: se encola la tarea procesar_imagen (Celery); no bloquea la respuesta.
3. Conversión real con Pillow: la tarea abre la imagen, la convierte a modo RGB/RGBA si hace falta, la redimensiona si supera 1920px de ancho (Lanczos) y la re-guarda como WebP calidad 80, sobrescribiendo el mismo render — el binario original que se subió no se conserva aparte.
4. Tamaños (imagen_render): miniatura, media y original — cada uno es una fila con su propio ancho/alto/bytes/checksum_sha256.
5. Servido:GET /imagenes-render/{id}/archivo devuelve el binario guardado en Postgres (modo autónomo, el que está activo hoy) — o redirige a una URL si el render tiene url rellena (modo CDN/S3, preparado en el modelo pero sin activar).
Lo que NO existe todavía
No hay AWS S3, CloudFront ni ningún almacenamiento de objetos externo conectado — el campo url del render está pensado para eso, pero hoy siempre está vacío y se sirve el binario.
No hay comprobantes en PDF generados por el sistema.
No hay borrado automático de archivos huérfanos ni políticas de ciclo de vida.
Limitaciones y restricciones
Tamaño de la base: al guardar el binario en Postgres (BinaryField), la base de fotos crece con cada imagen — no hay un límite de tamaño de subida más allá de DATA_UPLOAD_MAX_MEMORY_SIZE (10 MB) en la configuración de Django.
Sin CDN: cada imagen se sirve directamente desde el backend, sin nodos de borde ni caché geográfico.
Reproceso: si procesar_imagen falla (imagen corrupta), reintenta hasta 3 veces; si sigue fallando, la imagen queda sin sus tamaños procesados.
Propósito del componente
Separar las fotos (pesadas, de crecimiento constante) de leon_principal, y servir siempre WebP optimizado sin importar en qué formato subió la foto quien la cargó.
Por qué: con el volumen actual, guardar el binario en Postgres evita montar un servicio de almacenamiento aparte. Si el catálogo crece mucho, el modelo ya tiene el campo url preparado para migrar a S3/CDN sin cambiar el contrato de la API.