将Matplotlib绘图图像保存到Django模型中

11

出于学习目的,我正在制作一个使用Matplotlib制作图表的Web应用程序,并且希望将该绘图的图像保存到Plot模型的figure字段中。但是当我制作这个绘图时,它只会将空白图像保存到/media/figures/目录中。我按照其他帖子建议的方法做了这件事,但它并没有起作用。

更多信息 当我制作这张图时,图像以<_io.StringIO object at 0xb1ac69ec>这样的名称保存在我的Django项目的主目录中,但正如我所说,保存在模型中的图像是空白的。此外,我正在使用Python 2.7

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')

用于绘制图形并创建图示实例的文件。

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

绘制模型

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)

你能发布一下Plot模型的样子吗?ContentFile是做什么用的? - Jonathan
我添加了Plot模型,ContentFile/ https://docs.djangoproject.com/en/1.9/ref/files/file/#the-contentfile-class - kushtrimh
3个回答

7

您的代码存在以下几个问题:

  • 您不应该将图形保存到StringIO中,而应该使用io.BytesIO()。这是因为PNG文件的内容不是人类可读的文本,而是二进制数据。

  • 另一个问题是在将BytesIO(您的代码中是StringIO)传递给savefig时如何处理它。 BytesIO没有与文件关联(这就是拥有内存中文件类型对象的整个目的),因此它没有文件名 - 这就是我认为您想通过u'%s' % figure表达式来获得的内容。相反,只需将其写入文件类型对象本身即可。

  • 第三,在使用ContentFile之前,请使用django.core.files.images.ImageFile。此外,初始化它时使用BytesIO对象本身,而不是其字节值。

您的代码中相关部分应该是这样的:

figure = io.BytesIO()
plt.plot(xvalues, yvalues)
plt.savefig(figure, format="png")
content_file = ImageFile(figure)

我尝试了这个,但是同样的事情仍在发生,我在帖子中添加了一些更多的信息,也许会有所帮助。 - kushtrimh
1
我将在Django项目中尝试它,因为我从代码中没有立即看到其他错误。然而,一旦您更新代码以直接保存绘图到“figure”,那个关于保存一个带有有趣名称的文件到您的项目目录的部分应该消失了。有趣的名称正是那个错误的u'%s' % figure表达式的结果。 - Thomas Lotze
1
哦,等等,我发现了另外一件事情:也许你不应该直接使用ContentFile,而是使用它的一个子类,该子类还混合了django.core.files.images.ImageFile。如果这样做没有帮助,你还可以在调用savefig之后放置一个调试器,以检查figure.getvalue()是否看起来像你期望的图像文件内容。这至少可以告诉你错误是在绘图还是在Django方面。 - Thomas Lotze
我尝试使用ImageFile,但是同样的事情正在发生,当我使用plt.savefig(figure, format="png")时,我得到了unicode argument expected, got 'str',所以我使用plt.savefig(unicode(figure), format="png") - kushtrimh
请参见更新后的答案。如果还是不清楚,请再次询问。 - Thomas Lotze
非常感谢您的帮助! - kushtrimh

1

0
工作版本:
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接