Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save Matplotlib plot image into Django model

For learning purposes I'm making a webapp that make plots with matplotlib, and I want to save the image of that plot into the figure field of the Plot model but when I make a plot all it does it saves a blank image into the /media/figures/ directory. I did this the way an other post suggested but it's not working.

More Info When I make the plot, the plot image gets saved in the main directory of my django project with a name like <_io.StringIO object at 0xb1ac69ec>, but as I said the plot image that gets saved in the model is blank. Also im using Python 2.7 if that matters.

def simple_plot(request):
    if request.method == "POST":
        name = request.POST.get("name")
        xvalues = request.POST.get("xvalues")
        yvalues = request.POST.get("yvalues")
        plots.simple_plot(request, name, xvalues, yvalues)
        messages.success(request, "Plot created!")
        redirect("plots:create")
    return render(request, 'plots/simple.html')

The file which plots are made and plot instances created.

def simple_plot(request ,name, xvalues, yvalues):
    file_name = name+".png"
    xvalues = [int(x.replace(" ","")) for x in xvalues.split(",")]
    yvalues = [int(y.replace(" ","")) for y in yvalues.split(",")]

    figure = io.StringIO()
    plot = plt.plot(xvalues, yvalues)
    plt.savefig(u'%s' % figure, format="png")

    content_file = ContentFile(figure.getvalue())
    plot_instance = Plot(name=name, user=request.user)
    plot_instance.figure.save(file_name, content_file)
    plot_instance.save()
    return True

Plot Model

class Plot(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    figure = models.ImageField(upload_to='figures/')
    timestamp = models.DateTimeField(auto_now_add=True)
like image 344
kushtrimh Avatar asked Feb 23 '16 15:02

kushtrimh


2 Answers

There are several problems with your code:

  • You should not save the figure to a StringIO but instead, a io.BytesIO(). This is because the contents of the PNG file isn't human-readable text but binary data.

  • Another problem is how you handle the BytesIO (StringIO in your code) when passing it to savefig. A BytesIO isn't associated with a file (that's the whole point of having an in-memory file-like object), so it doesn't have a file name – which is what I suppose you want to get at by that u'%s' % figure expression. Instead, just write to the file-like object itself.

  • Third, use a django.core.files.images.ImageFile instead of ContentFile. Also, initialise it with the BytesIO object itself, not its bytes value.

The relevant portion of your code then becomes:

figure = io.BytesIO()
plt.plot(xvalues, yvalues)
plt.savefig(figure, format="png")
content_file = ImageFile(figure)
like image 91
Thomas Lotze Avatar answered Sep 21 '22 18:09

Thomas Lotze


you might also want to try the plotly library - they have a js script that you can add to the html (https://plot.ly/javascript/getting-started/) and you can always serialize the arrays needing to be imported into the graph

like image 33
Alicia Avatar answered Sep 17 '22 18:09

Alicia