Django Suite IV: Hablemos un poco de caché.
Decía Phil Karlton "Solo hay dos cosas dificiles en las ciencias de la computación: invalidación de caché y nombrar cosas."
En esta entrega de la serie Django Suite - después de mas de un año de ausencia - hablaré sobre el framework de caché de Django.
Principios de caché
Cuando hacemos un sitio, especialmente si estamos comenzando no tomamos en cuenta el desempeño del sitio y la cantidad de tiempo de procesamiento por cada petición. El tiempo de procesamiento de un sitio está afectado por varios factores el mas importante la cantidad de consultas que realizamos a la base de dato por cada request, generalmente estas varían según la complejidad de nuestras vistas y el tipo de usuario (anónimo o autenticado) que tengamos en el sitio.
Para mejorar el desempeño del sitio se usa el caché que no es mas que un almacenamiento rápido, generalmente en RAM, que nos evita recaer en operaciones de cálculo pesadas como lo son hacer muchas consultas de base de datos la lógica de las vistas y el renderizado de las mismas en la plantilla que normalmente son realizadas haciendo lectura/escritura de disco duro que agrega mas tiempo a la receta.
Hacer caché de datos no es tan dificil, lo dificil es saber cuando los datos cacheados ya no son válidos y debemos de recalcular de nuevo para construir un nuevo caché, de ahí la frase del inicio del post.
Caché framework
Django contiene un framework de caché que abstrae las operaciones básicas, sin que nosotros tengamos que preocuparnos por el motor de caché que usemos, por defecto Django soporta Memcached, caché en base de datos, caché en el sistema de archivos y memoria local, siendo no muy recomendados estos últimos tres.
Pero como todo en Django la comunidad da soporte para otros motores de caché opensource muy buenos ademas de memcached, como por ejemplo:
- Varnish con django-varnish
- Redis con django-redis-cache
Caché de vistas
Django nos provee con el decorador cache_page para hacer caché a una vista, el decorador lo que hace es lo siguiente.
Si la vista ha sido cargada por primera vez, hace todas las operaciones y la guarda en caché con tiempo de vencimiento designado en la llamada del decorador, caso contrario carga el resultado de la vista desde caché.
Ejemplo:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ....
El valor de 60 * 15 representa la duración de la validez del caché en segundos, en este caso 15 minutos, se acostumbra mucho para asignar valores de vencimiento poner multiplicaciones de la cantidad de segundos en un minuto (60) por el número de minutos deseado.
En el caso que la vista tuviera parámetros como un mi_vista(request, post_id) el caché ser haría de manera individual
por cada valor de post_id
que tengamos presente. Mas detalles de esto en la documentación oficial.
Caché de fragmentos de plantillas
Fragmentos plantillas también pueden ser cacheadas, esto es especialmente útil para fragmentos que repetimos en casi todas las páginas, por ejemplo elementos de menú dinámicos, columnas de 'últimas noticias' o 'últimos post' en caso de ser un blog.
Ejemplo:
{% load cache %} {% cache 500 menu %} .. mi super menu de página .. {% endcache %}
Igual acá la template tag de cache acepta como parámetros la duracion en segundos, el nombre del fragmento y el conjunto de variables que deseamos establecer en el cache.
Este conjuto de variables es tremendamente útil para hacer caché en diversas situaciones tales como si el usuario está o no autenticado, el lenguaje del navegador del usuario ( en caso que usemos la opción de internacionalización y localización del sitio ), valor de alguna varible que usemos que puede modificar el resultado de la plantilla entre otros.
Ejemplo en código fuente de la vida real acá.
Generalmente la mejor documentación es el código fuente, y para verdaderamente entender y jugar con esta template tag recomiendo que vean como está hecha en este archivo
Caché manual
Si queremos verdaderamente sacar provecho al framework de caché de django tenemos que tomar ventaja del API de caché manual. Los motores de caché se basan en almacenamiento de llave-valor (hashmap) o en lo que python seríá un equivalente al tipo diccionario.
El api de caché es bastante simple, podemos ver su uso en el siguiente código fuente:
#importamos el objeto caché from django.core.cache import cache #guardamos algo en el cache con expiración de 60 segundos cache.set('a', 'un valor cacheado', 60) #leemos algo desde cache valor = cache.get('a') #borramos 'a' del cache cache.delete('a')
Básicamente esa es el API de bajo nivel, si nosotros ponemos como valor de tiempo 0 el caché nunca se vencerá y tendremos que eliminarlo manualmente a posteriori.
Trucos con el caché manual.
El caché manual es tremendamente útil usándolo en conjunto con modelos. Un ejemplo sencillo y fácil de implementar es un menú dinámico, recientemente programé uno que se veía algo así:
#models.py from django.db import models from django.core.cache import cache class Menu(models.Model): titulo = models.CharField(max_length=50) url = models.URLField() peso = models.PositiveIntegerField(default=0, help_text="peso del elemento del menú, entre mayor sea el número mas hacia el fondo estará el elemento") def save(self, *args, **kwargs): #primero guardamos return_value = super(ElementoNavegacion, self).save(*args, **kwargs) #invalidamos el cache antiguo de menu cache.delete('menu') #creamos el nuevo cache refrescado cache.set('menu', Menu.objects.all(), 0) return return_value def __unicode__(self): return "%s (%s)" % (self.titulo, self.url)
Se creó un context manager para tener la lista del menú en todas las plantillas.
#context manager para el menú. from django.core.cache import cache from models import Menu def menu_context_manager(request): #probamos si esta el elemento en caché valor = cache.get('menu') #si no está leemos de la base de datos if not valor: valor = Menu.objects.all() #lo cacheamos cache.set('menu', valor, 0) return {'menu': valor}
Haciendo esto se ahorran un par de consultas a la base de datos y por ende la velocidad de carga del sitio aumenta.
Despedida
En resumen hemos visto una introducción al cache framework de Django, espero que les haya servido y tendré que hacer un post de continuación para exponer otros casos de uso del caché manual y trucos con el API, además del caché al lado del servidor de archivos estáticos y del lado del navegador.
Comments