Mostrar gráficos generados con Matplotlib en Django

Introducción

Algo que me preguntan muchos mis alumnos de Django es si es posible generar gráficos en Python y mostrarlos en un template.

La respuesta es sí, pero es un poco lioso porque hace falta "renderizar" el canvas de las figuras de Matplotlib sobre un buffer de bytes.

No voy a enseñar nada de Matplotlib porque eso es harina de otro costal, aquí dejo mis apuntes online, pero sí el proceso para mostrar el gráfico.

Creando la vista generadora

La idea de mostrar gráficos con Django se basa en acceder a una url de nuestro proyecto, por ejemplo /plot/, y en lugar de renderizar un template, responder con la imagen del gráfico generada en tiempo de ejecución:

Al acceder a la URL que genera el gráfico, ésta devuelve una imagen PNG en lugar de un documento HTML.

El snippet para la vista de Django sería el siguiente:

views.py

import io
import matplotlib.pyplot as plt

from django.http import HttpResponse
from django.shortcuts import render
from matplotlib.backends.backend_agg import FigureCanvasAgg
from random import sample


def home(request):
    return render(request, "core/home.html")


def plot(request):
    # Creamos los datos para representar en el gráfico
    x = range(1,11)
    y = sample(range(20), len(x))

    # Creamos una figura y le dibujamos el gráfico
    f = plt.figure()

    # Creamos los ejes
    axes = f.add_axes([0.15, 0.15, 0.75, 0.75]) # [left, bottom, width, height]
    axes.plot(x, y)
    axes.set_xlabel("Eje X")
    axes.set_ylabel("Eje Y")
    axes.set_title("Mi gráfico dinámico")

    # Como enviaremos la imagen en bytes la guardaremos en un buffer
    buf = io.BytesIO()
    canvas = FigureCanvasAgg(f)
    canvas.print_png(buf)

    # Creamos la respuesta enviando los bytes en tipo imagen png
    response = HttpResponse(buf.getvalue(), content_type='image/png')

    # Limpiamos la figura para liberar memoria
    f.clear()

    # Añadimos la cabecera de longitud de fichero para más estabilidad
    response['Content-Length'] = str(len(response.content))

    # Devolvemos la response
    return response

Esto nos generaría el gráfico que os muestro en la imagen de arriba, que cambiará cada vez que recargamos la página con F5 porque contiene datos aleatorios tomados del módulo random.

Gráficos como imágenes

Lo bueno es que podemos insertar esta URL como si fuera una imagen:

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Test plot</title>
</head>
<body>
    <h2>Gráfico embebido por imagen</h2>
    <p>Presionar F5 para generar nueva muestra: </p>
    <img src="/plot/" width="600px" />
</body>
</html>

Quedando finalmente la página principal de la siguiente forma:

Si a esta idea le sumamos parámetros, ya sea por GET o POST que podemos capturar en la vista, entonces podemos añadir dinamismo y generar los gráficos como nosotros queramos. Por ejemplo pasando un parámetro GET llamado type /plot/?type=1 donde luego con request.GET.get['type'] podamos seleccionar el tipo de gráfico o lo que se os ocurra.

 

Django 2.2
09/06/2019

Recursos disponibles

attach_file
Código del tutorial