Making charts and output them as images to the browser in Django

Simple graph made with matplotlib
Lets say you are working on a website made in Django. And you want to make some nice looking graphs real time, as images from dynamic data. This can be done by using the python 2D graph library matplotlib. The library can be found in the debian package python-matplotlib. A simple graph showing a sine curve, seen to the right, can be generated in regular python using the following code(taken from this example):
from pylab import * t = arange(0.0, 2.0, 0.01) s = sin(2*pi*t) plot(t, s, linewidth=1.0) xlabel('time (s)') ylabel('voltage (mV)') title('About as simple as it gets, folks') grid(True) show() |
Output graph to browser from a Django view
If you want to output this graph as a PNG image to the browser from a view in Django, you can store the image in a string buffer and output this buffer using the HttpReponse class in Django and set the mime type to image/png.
from django.http import HttpResponse from django.shortcuts import render from matplotlib import pylab from pylab import * import PIL, PIL.Image, StringIO def index(request): return render(request, 'yourapp/index.html') def showimage(request): # Construct the graph t = arange(0.0, 2.0, 0.01) s = sin(2*pi*t) plot(t, s, linewidth=1.0) xlabel('time (s)') ylabel('voltage (mV)') title('About as simple as it gets, folks') grid(True) # Store image in a string buffer buffer = StringIO.StringIO() canvas = pylab.get_current_fig_manager().canvas canvas.draw() pilImage = PIL.Image.frombytes("RGB", canvas.get_width_height(), canvas.tostring_rgb()) pilImage.save(buffer, "PNG") pylab.close() # Send buffer in a http response the the browser with the mime type image/png set return HttpResponse(buffer.getvalue(), content_type="image/png") |
Let’s say you want to display this image on your index page, you can have something like this on your index.html template:
<h1>Hello world of django + matplotlib</h1> <img src="/showimage/" alt=""> |
Assuming you have an urls.py with something like this:
from django.urls import include, path from . import views urlpatterns = [ path('', views.index, name='main-view'), path('/showimage/', views.showimage, name='showimage'), |
Hi, While running this code in django , I am getting an error . “No Module found : StringIO”. I also used “from io import StringIO” but I didn’t work. I used some try and except block but didn’t work. Can You tell how can I run this , I have tried everything
Hi. In Python 3, this library seems to have been moved to the io module: https://docs.python.org/3/library/io.html
Thus, you should write
import io
and then instead of buffer = StringIO.StringIO() use buffer = io.StringIO()
If you use django 1.11+, you can choose django-matplotlib field (http://django_matplotlib.readthedocs.org/). This is virtual field (it doesn’t generate a column in the db) which can be easily integrated to Django Admin.
could see the full view.py and the main html page please?
What would be helpful is an FULL example from a CBV (meaning views.py + template file).
Thanks for the Notes.
But how to render the image to html file ?
I am unable pass it html file. Please help me
same as praveen… how to write the html file for this
Lets say the URL pointing to the view show_image is /show_image/, then you add the following in your HTML file:
I still do not understand. My index page is on localhost:8000/. in my urls.py it is on path(‘ ‘,views.index, name =’index’). I have a function def index(request). what do I write in index.html to display this image there?
Hi Sajwal, did you resolve this issue? Even. i dont understand what to write in index.html to display the image generated from matplotlib or seaborn.
@Erik Smistad : can you be more specific?
I have updated the post with some more details. Hopefully it will help you understand how to set up everything.
It gives me this error when I followed your logic:
Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘NSWindow drag regions should only be invalidated on the Main Thread!’
f you are running a webserver and using it to save Matplotlib make sure to set the backend to a non-interactive one (matplotlib.use(‘agg’) or matplotlib.pyplot.switch_backend(‘Agg’)) so that your server does not try to create (and then destroy) GUI windows that will never be seen (or if they are will be more of a nuisance).
after import matplotlib
matplotlib.use(‘agg’)
I understand now. Thank you. Just somethings that worked for me are that I had to change
buffer = StringIO.StringIO() (we can use just StringsIO() in python 3, imported from io)
to
buffer = BytesIO()
which I had to import from io
from io import BytesIO
also for some reason I had to add
matplotlib.use(‘Agg’)
so that I did not get that NSInternalInconsistencyException’, reason: ‘NSWindow drag regions should only be invalidated on the Main Thread!’ error
Anyways, thank you so much. Great post!
Couple of suggestions:
As of April 2018:
from string is replaced with frombytes
user content_type instead of mimetype
Thanks!
You don’t need io or cStringIO. You can just do:
canvas = FigureCanvasAgg(f)
response = HttpResponse(content_type=’image/png’)
canvas.print_png(response)
plt.close(f)
return response
-pylab.cose()
+pylab.close()
Can you show an example that involves creating and sending multiple chart images to the same page – I cannot get this to work.
Thanku it was really helpful.
Hi! Thanks for your website, it is really useful. About the output graph to browser from a Django view, I wonder if instead of using HttpResponse we can use render_to_response and represent the graph in a specific html page. I’ve done something like this:
# serialize to HTTP response
response = HttpResponse(buffer.getvalue(), mimetype=”image/png”)
return render_to_response(‘eQL/dev/showImage.html’, {‘response’:response})
But I don’t know how to plot it in the html file.
Thanks
Images on websites are sent in separate http responses, so I don’t think that’s possible
Then what would you suggest if we needed to display the image in a specific, html template??
use render instead of http response
whats the book you prefer to learn python with that
I don’t have a Python book, I use the manual on the web instead
you could use cStringIO…its faster
I tried using cStringIO instead of StringIO and measured execution time using datetime. Both buffer types ran in about 0.25 seconds. I think this is because the code only performs one large write to the buffer. If you have a buffer with several writes and reads, cStringIO will probably be notably faster