1

How can I store a matplotlib plot in a Django BinaryField then render it directly to a template?

Chris
  • 5,664
  • 6
  • 44
  • 55

1 Answers1

4

These are the commands I use to save a matplotlib image to a BinaryField type:

The field (I haven't seen anything saying storing binary in a separate table is good practice):

class Blob(models.Model):
    blob = models.BinaryField(blank=True, null=True, default=None)

To generate and save the image:

import io
import matplotlib.pyplot as plt
import numpy as np
from myapp.models import Blob

# Any old code to generate a plot - NOTE THIS MATPLOTLIB CODE IS NOT THREADSAFE, see http://stackoverflow.com/questions/31719138/matplotlib-cant-render-multiple-contour-plots-on-django
t = np.arange(0.0, gui_val_in, gui_val_in/200)
s = np.sin(2*np.pi*t)
plt.figure(figsize=(7, 6), dpi=300, facecolor='w')
plt.plot(t, s)
plt.xlabel('time (n)')
plt.ylabel('temp (c)')
plt.title('A sample matplotlib graph')
plt.grid(True)

# Save it into a BytesIO type then use BytesIO.getvalue()
f = io.BytesIO()  # StringIO if Python <3
plt.savefig(f)
b = Blob(blob=f.getvalue())
b.save()

To display it, I create the following in myapp/views.py:

def image(request, blob_id):
    b = Blob.objects.get(id=blob_id)
    response = HttpResponse(b.blob)
    response['Content-Type'] = "image/png"
    response['Cache-Control'] = "max-age=0"
    return response

Add to myapp/urls.py:

url(r'^image/(?P<blob_id>\d+)/$', views.image, name='image'),

And in the template:

<img src="{% url 'myapp:image' item.blob_id %}" alt="{{ item.name }}" />
Chris
  • 5,664
  • 6
  • 44
  • 55
  • `value.getvalue` should be `f.getvalue()` (although I prefer to render/store as a base64 array ;)) – Sayse Feb 22 '16 at 11:48
  • Oops thanks, corrected. Any reason for preferring base64 other than it is pre Django-1.6 compatible? Seems the natural choice and worked well. – Chris Feb 22 '16 at 12:53
  • 1
    Chris, because it doesn't require a second url to be resolved (and in your case another database query), you can have a [base64 array as an image source](http://stackoverflow.com/q/1207190/1324033). – Sayse Feb 22 '16 at 13:01
  • 1
    Btw, I'd strongly recommend you see my other question, [Matplotlib can't render multiple contour plots on Django](http://stackoverflow.com/q/31719138/1324033), your current chart creation code looks like its going to fall into the same issue – Sayse Feb 22 '16 at 13:23
  • 1
    I haven't seen this as my scripts run in separate Celery processes but your option 2 on that question is a fantastic find and clearly better code so I'll update. (I'm sticking with the redundant URL call to be compatible with Dolphin and Android stock browsers.) – Chris Feb 22 '16 at 14:23