11

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)
2
  • Could you post what the Plot model looks like? What does ContentFile do? Commented Feb 25, 2016 at 16:28
  • I added the Plot model, ContentFile/ docs.djangoproject.com/en/1.9/ref/files/file/… Commented Feb 25, 2016 at 17:03

3 Answers 3

7
+50

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)
Sign up to request clarification or add additional context in comments.

6 Comments

I tried this but the same thing is happening, I added some more info in the post maybe it'll help.
I'm going to try it out in a Django project as I cannot see any other mistake immediately from the code. That part about a file with a funny name being saved to your project directory should be gone, however, once you update your code to directly save to plot to figure. The funny name is exactly the result of that mistaken u'%s' % figure expression.
Oh wait, I do spot something else: Maybe you should not directly use ContentFile but a subclass of it that also mixes in django.core.files.images.ImageFile. If that doesn't help, you can also place a debugger after the call to savefig to check whether figure.getvalue() looks like the content of the image file you expect. That yould at least tell you whether the error is in the plotting or on the Django side.
I tried using the ImageFile but the same thing is happening, also when I use plt.savefig(figure, format="png") I get unicode argument expected, got 'str' so I'm using plt.savefig(unicode(figure), format="png")
See the updated answer. If it still doesn't help, ask again.
|
1

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

Comments

0

Working version:

import io
from django.core.files.base import ContentFile

figure = io.BytesIO()
plt.savefig(figure, format='png')
content_file = ContentFile(figure.getvalue())
one_round.chart.save('chart.png', content_file)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.