19

So, bear with me as I'm VERY new to Django, Python, and web-development in general. What I want to do is display a graph that I've made using matplotlib. I had it working to where the home page automatically redirects to the png of the graph (basically a tab in the browser with the graph displayed). However, now what I want is to see the actual homepage with the graph simply embedded. In other words, I want to see the navigation bar, etc. and then the graph in the body of the site.

So far, I've searched and I sort of have an idea of how to accomplish this. What I'm thinking is having a special view that simply returns the graph. Then, somehow accessing this png image from an img src tag in my template that I will use to display my data.

Graph Code:

from django.shortcuts import render
import urllib
import json
from django.http import HttpResponse
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import datetime as dt
import pdb

def index(request):
    stock_price_url = 'https://www.quandl.com/api/v3/datatables/WIKI/PRICES.json?ticker=GOOGL&date.gte=20151101&qopts.columns=date,close&api_key=KEY'

    date = []
    price = []

    #pdb.set_trace()
    source_code = urllib.request.urlopen(stock_price_url).read().decode()

    json_root = json.loads(source_code)
    json_datatable = json_root["datatable"]
    json_data = json_datatable["data"]

    for day in json_data:
        date.append(dt.datetime.strptime(day[0], '%Y-%m-%d'))
        price.append(day[1])

    fig=Figure()
    ax = fig.add_subplot(1,1,1)

    ax.plot(date, price, '-')

    ax.set_xlabel('Date')
    ax.set_ylabel('Price')
    ax.set_title("Google Stock")

    canvas = FigureCanvas(fig)
    response = HttpResponse(content_type='image/png')
    #canvas.print_png(response)
    return response

Template Code:

{% extends "home/header.html" %}
  {% block content %}
  <p>Search a stock to begin!</p>
  <img src="home/graph.py" />

  {% endblock %}

What I'm getting now:

Current Page

rigotre
  • 942
  • 2
  • 7
  • 14
  • Found the answer to my question [here.](http://www.extragravity.com/?entry_id=8) – rigotre Nov 11 '16 at 04:10
  • could you kindly elaborate where I might find the solution in your given link? thanks :) – sphoenix Aug 19 '17 at 18:13
  • 2
    @sphoenix It's been a while since I've worked with Django but I believe it was this [here](http://www.extragravity.com/blog/2014/01/04/matplotlib-django/) – rigotre Aug 23 '17 at 21:40

5 Answers5

24

I know this question is tagged as matplotlib, but I needed to do the same thing and I have found plotly to be easier to use and visually appealing as well. You can simply plot a graph and then in the view get the html code of the graph as :

# fig is plotly figure object and graph_div the html code for displaying the graph
graph_div = plotly.offline.plot(fig, auto_open = False, output_type="div")
# pass the div to the template

In the template do:

<div style="width:1000;height:100">
{{ graph_div|safe }}
</div>
Deepak Saini
  • 2,810
  • 1
  • 19
  • 26
9

I searched quite a bit until I found a solution that worked for me when it comes to rendering a matplotlib image on a Django page. Often, simply a lengthy string was printed instead of a visualization being produced. So, what finally worked for me was the following:

First, the imports:

import matplotlib.pyplot as plt
from io import StringIO
import numpy as np

The dummy function returning a graphic is the following:

def return_graph():

    x = np.arange(0,np.pi*3,.1)
    y = np.sin(x)

    fig = plt.figure()
    plt.plot(x,y)

    imgdata = StringIO()
    fig.savefig(imgdata, format='svg')
    imgdata.seek(0)

    data = imgdata.getvalue()
    return data

This one can be called by some other function using and rendering the image returned by return_graph():

def home(request):
    context['graph'] = return_graph()
    return render(request, 'x/dashboard.html', context)

And in the dashboard.html file, the graphic is embedded by the following command:

{{ graph|safe }}
Daniel B.
  • 659
  • 1
  • 9
  • 17
4

I think you should uncoment this line:

#canvas.print_png(response)

You can easily return plot using django HttpResponse instead using some extra libs. In the matplotlib there is a FigureCanvasAgg which gives you access to canvas where plot is rendered. Finaly you can simple return it as HttpResonse. Here you have very basic example.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg
from django.http import HttpResponse

def plot(request):
    # Data for plotting
    t = np.arange(0.0, 2.0, 0.01)
    s = 1 + np.sin(2 * np.pi * t)

    fig, ax = plt.subplots()
    ax.plot(t, s)

    ax.set(xlabel='time (s)', ylabel='voltage (mV)',
           title='About as simple as it gets, folks')
    ax.grid()

    response = HttpResponse(content_type = 'image/png')
    canvas = FigureCanvasAgg(fig)
    canvas.print_png(response)
    return response
Janek
  • 372
  • 4
  • 11
  • This is a simple and easy to understand response. I am trying to do the same as OP. I have my view pointing to a template which does a few things to display data from the model, but I am wondering how to incorporate this into that view? At the end I return return: "render(request, 'viewer.html', context), where context is the object from my model that i pass to the template [context = {'modelObj': modelObj, 'displayFields': form}]. how can i also send the graph like above, in addition to the context i already send? thank you in advance – user20408 Oct 10 '18 at 01:51
  • 1
    Note that the above example can cause the following error: "ValueError: fname must be a PathLike or file handle". This question addresses some of the reasons for this happening: https://stackoverflow.com/questions/49542459/error-in-django-when-using-matplotlib-examples –  Oct 14 '19 at 13:57
1

My setup:

Django==2.2.2
windrose==1.6.8

I am just sharing a solution I found online. I made it work with windrose and django (thanks to some article writer). And this

import base64
import io
from matplotlib import pyplot as plt

flike = io.BytesIO()
plt.savefig(flike)
b64 = base64.b64encode(flike.getvalue()).decode()
return render(request, template_name='details_wind.html', context={'wind_rose': wind_rose_image})

In the template, show it as an image:

<img src='data:image/png;base64,{{ wind_rose }}'>
Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
0

If you want to embed matplotlib figures to django-admin you can try django-matplotlib field. This field doesn't create a column in the database, but is rendered as a regular field. You don't need to customize/subclass admin.ModelAdmin, just use this field in your models and define how the figure will be generated.

bubble
  • 1,634
  • 12
  • 17