SecNot

feb 26, 2014

Introducción a easy_thumbnails

Una situación muy común en cualquier aplicación web es la necesidad de mostrar miniaturas de las imágenes subidas por los usuarios. Esto se puede conseguir usando CSS para escalar la imagen, cortarla, y hasta convertirla a blanco y negro, pero es un desperdicio de ancho de banda.

En Django existen varias apps para simplificar la tarea, pero mi preferida por su simplicidad y facilidad de integración es easy-thumbnails

Instalación

Usa pip para instalar en paquete:

$ pip install easy_thumbnails

Añade easy_thumbnails a la lista de aplicaciones instaladas en el archivo settings.py:

INSTALLED_APPS = (
    ...
    'easy_thumbnails',
)

Uso

Easy-thumbnails funciona generando dinámicamente miniaturas desde las imágenes originales. Cuando llega una petición y la miniatura no existe, o ha sido modificada, una nueva miniatura es generada y salvada.

Para mostrar las miniaturas en los templates, es necesario usar los tags proporcionados por easy_thumbnails, thumbnail y thumbnail-url:

{# Cargamos los tags de easy_thumbnails #}
{% load thumbnail %}
....
<div class="producto">
    <img src="{% thumbnail producto.foto 90x90 crop %}"/>
    <h3>{{ producto.nombre }}</h3>
</div>

En el tag indicamos la imagen fuente, el tamaño al que deseamos convertir la imagen, seguido de cualquier opción adicional, en este caso indicamos que corte la imagen si es necesario para llegar al tamaño seleccionado.

También es posible indicar solo una de las dimensiones de la imagen, de manera que la otra se escale para mantener las proporciones, sin deformar la imagen ni cortar los bordes.

{% load thumbnail %}
....
<div class="producto">
    <img src="{% thumbnail producto.foto 90x0 %}"/>
    <h3>{{ producto.nombre }}</h3>
</div>

Para simplificar la tarea y evitar errores, podemos crear un alias para las distintas configuraciones de las miniaturas, para ello hay que crear un diccionario llamado THUMBNAIL_ALIASES en settings.py, que contiene un las opciones de cada alias:

THUMBNAIL_ALIASES = {
    '': {
        'producto': {'size': (90, 90), 'crop': True},
        'cartel': {'size': (90, 0),},
    },
}

Luego podemos usar estos alias con:

{# Cargamos los tags de easy_thumbnails #}
{% load thumbnail %}
....
<div>
    <img src="{% producto.foto|thumbnail_url:'cartel' %}"/>
    <h3>{{ producto.nombre }}</h3>
</div>

ThumbnailerImageField

Este campo nos hace aún más fácil manejar las direcciones de las miniaturas en en template, y permite gestionar cuando son generadas. Para usarlo solo es necesario substituir en los modelos ImageField por ThumbnailerImageField:

from django.db import models
from easy_thumbnails.fields import ThumbnailerImageField

class Producto(models.Model):
    """Mi modelo de un producto"""
    foto = ThumbnailerImageField()
    text = models.TextField(text)

En los templates puedes obtener la dirección de la miniatura fácilmente usado su alias:

{% load thumbnail %}
....
<div>
    <img src="{{ producto.foto.cartel.url }}"/>
    <h3>{{ producto.nombre }}</h3>
</div>

Si la aplicación requiere que las miniaturas sean generadas en el momento que las imágenes son subidas, se pueden usar los manejadores de señales en models.py:

from easy_thumbnails.signals import saved_file
from easy_thumbnails.signal_handlers import generate_aliases_global

saved_file.connect(generate_aliases_global)

Integración con Amazon S3

En caso de que necesites usar Amazon S3 para almacenar imágenes, ya sea para servirlas directamente, o como backup, configurar easy_thumbnails es muy sencillo.

Primero necesitamos instalar django-storage y la biblioteca boto de python para S3.

$ pip install django-storage
$ pip install boto

Una vez instalados configuramos easy_thumbnails para indicarle que tiene que usar S3 como almacenamiento, en settings.py hay que añadir:

THUMBNAIL_DEFAULT_STORAGE ='storages.backends.s3boto.S3BotoStorage'
THUMBNAIL_BASEDIR = '_miniaturas/'

Ademas de indicar que use S3, lo he configurado para que guarde las miniaturas en un directorio aparte llamado '_miniaturas/', esto hace que sean más sencillas de gestionar.

Tras esto configuramos Django para que también guarde la imagen original en S3, añadimos las credenciales AWS, y el nombre del bucket a usar:

DEFAULT_FILE_STORAGE    = 'storages.backends.s3boto.S3BotoStorage'
AWS_S3_SECURE_URLS      = False     # Usar http en lugar de https
AWS_QUERYSTRING_AUTH    = False     # authentication sencilla
AWS_ACCESS_KEY_ID       = 'Tu_ACCESS_KEY_ID'
AWS_SECRET_ACCESS_KEY   = 'Tu_SECRET_ACCESS_KEY'
AWS_STORAGE_BUCKET_NAME = 'nombre_bucket'

Si usas S3 es recomendable usar señales para que easy_thumbnails calcule las miniaturas en el momento de creación de las imágenes.

Por último sincronizamos la base de datos

$ python manage.py syncdb