User Guide

KickStart

Lets see how to insert a simple Matplotlib graph in an existing Django webpsite using Plottings.

First, use the command pip to install the package to your local development environment:

pip install django-plottings

Then add the module to the INSTALLED_APPS in the settings.py file of your project.

INSTALLED_APPS = [
...
'plottings',
...
]

Let’s see how to render a plot to be passed to a template as context variable. First we need to import Matplotlib and the PNGValue class of Django Plottings to the views.py module of your app, and disable rendering to the screen:

import numpy as np
import matplotlib.pyplot as plt
from plottings import PNGValuePlot
from django.shortcuts import render
matplotlib.use("Agg")

Then create the class that renders the plot:

class SimplePlotToValue(PNGValuePlot):
    def plotter_function(self, data, **options):
        np.random.seed(2)
        fig, ax = plt.subplots()
        ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7,
                mfc='orange')
        ax.grid()
        return figure

def plot(request, **kwargs):
    return render(request, "plot.html", {"plot": SimplePlotToValue()}

Setup the template plot.html in your apps template directory:

<html>
<body>
  <h1>My plot</h1>
  {{ plot }}
</body>
</html>

And finally add a route to your plot view in your apps url.py

from django.urls import path
from . import views

urlpatterns = [
    path("plot", views.plot, name="plot"),
    ]

Now you can run it from your Python environment:

./manage.py runserver

And now open your browser and point it to your local app and then you should view the plot in your screen.

How to build a Plot

The plotting process is mainly done with the use of plotting classes each one specialized in one type of output format and kind of rendering. Available formats are two: SVG and PNG and they can be rendered as View, Value or File, this gives us a family of six plotting classes:

  • PNGViewPlot: A cached Django View class that returns a PNG.

  • SVGViewPlot: A cached Django View class that returns an SVGZ file.

  • PNGFilePlot: Builds a Django File object containing a PNG image to be saved to storage. Useful to run it in background jobs.

  • SVGFilePlot: Builds a Django File object containing a SVGZ image to be saved to storage. Useful to run it in background jobs.

  • PNGValuePlot: A cached python variable containing a plot object ready to be rendered within a template as a PNG image encoded in Base64.

  • SVGValuePlot: A cached python variable containing a plot object ready to be rendered within a template as an inlined SVG image.

These classes share all the same plotting methods that are:

  • An static method plotter_function(data, **options), that returns a Matplotlib figure object. The idea is not to implement the function here but to bring it from an interactive development environment like iPython Jupyter Notebooks.

  • A method get_plot_data() that is used to provide data to the plotter_function().

  • Another method get_plot_options() that returns a dictionary of values ready to be passed as named arguments to the plotter_function() to customize its behaviour.

So to build a graph you have to first select the plotting class you want to use then copy&paste the plotter_function() and refactor it to obtain data from the data parameter and personalization from the named arguments.

Then implement how to obtain the data from Django models or whatever and build the get_plot_data() and collect the options with get_plot_options(). These steps might be different in a View class than in the Value and File classes.

Plotting to a View

Django Views are initialized each request and values are stored in the object as the object attributes request, args, kwargs. So the methods get_plot_options() and get_plot_data() must access these three attributes to build a response plot.

class ActivitiesPlot(PNGViewPlot):

    def get_plot_options(self):
        return {"color": request.GET.get("color", "blue")}

    def get_plot_data(self):
        activities = self.request.user.get_activities()
        return [ x.date for x in activities ]

Plotting to a Value

The Value class should be declared with the __init__() initialization method with the parameters needed to set the object attributes required by the get_plot_data() and get_plot_options() to pass the right parameters to the plotting function to render the image accordingly.

class ActivitiesPlot(PNGValuePlot):

    def __init__(self, activities, color="blue"):
        self.activities = activities
        self.color = color

    def get_plot_options(self):
        return {"color": self.color}

    def get_plot_data(self):
        return [ x.date for x in self.activities ]

Then the class is initialized within the view function or get_context_data() of the View object. The resulting plot object is passed to the template as another value to be rendered.

def activities_view(request, *args, **kwargs):
    activities = request.user.get_activities()
    a_plot = ActivitiesPlot(activities)
    return render(request, "activities.html", {"a_plot": a_plot})

Plotting to a File

The File class should be declared with the __init__() initialization method with the parameters needed to set the object attributes required by the get_plot_data() and get_plot_options() to pass the right parameters to the plotting function to render the image accordingly.

class ActivitiesPlot(PNGFilePlot):

    def __init__(self, activities, color="blue"):
        self.activities = activities
        self.color = color

    def get_plot_options(self):
        return {"color": self.color}

    def get_plot_data(self):
        return [ x.date for x in self.activities ]

To render the plot when its needed you just have to call the object with the and asign it to a model field and then save it to store the plot in storage and the reference to it in the database:

def task_save_activities(user_id):
    user = User.objects.get(id=user_id)
    activities = user.get_activities()
    a_plot = ActivitiesPlot(activities)
    user.activities_plot = a_plot.as_file()
    user.save()

Caching

Caching is implemented in the View and Value classes. You only need to implement the get_cache_key() method that returns an identifying value of your plot and to set the timeout you have to set the class attribute cache_timeout to the number of seconds of your choosing.

class ActivitiesPlot(PNGViewPlot):
    cache_timeout = 60 * 60 * 24 # Reload every 24h

    def get_cache_key(self):
        return f"activities_plot_{self.request.user.id}"

Post Processing

To modify the rendered image there is the process_image() method that takes an image in memory file and returns another.

Good Practices

It’s a good idea to have the code split in separated files in the Django app directory. The initial propossal is to place all matplotlib, numpy, pandas… code in plots.py file.

And don’t forget to matplotlib.use("Agg").