3

AIM

I am attempting to:

  1. Create a histogram,
  2. Store it temporary memory,
  3. Pass the image to the template.

I am having trouble with Step 3 above. I suspect that I am making a simple and fundamental error in relation to passing the context data to the template.

ERROR

HTML is rendering with a broken image tag.

CODE

Views.py

class SearchResultsView(DetailView):

   ...

   def get(self, request, *args, **kwargs):
        self.get_histogram(request)
        return super(SearchResultsView, self).get(request, *args, **kwargs)


    def get_context_data(self, **kwargs):
        context = super(SearchResultsView, self).get_context_data(**kwargs)
        return context


    def get_histogram(self, request):
        """ Function to create and save histogram of Hashtag.locations """
        # create the histogram
        plt.show()
        img_in_memory = BytesIO()
        plt.savefig(img_in_memory, format="png")
        image = base64.b64encode(img_in_memory.getvalue())
        context = {'image':image}
        return context

Results.html

<img src="data:image/png;base64,{{image}}" alt="Location Histogram" />

SOLUTION

In addition to issues with get and get_context_data as outlined by @ruddra below, another issue was that I had to decode the base64 string as a Unicode string. For more information, see here.

To do so, I included: image = image.decode('utf8')

So that, views.py looks like this:

def get_histogram(self, request):
    # draw histogram
    plt.show()
    img_in_memory = BytesIO()
    plt.savefig(img_in_memory, format="png") # save the image in memory using BytesIO
    img_in_memory.seek(0) # rewind to beginning of file
    image = base64.b64encode(img_in_memory.getvalue()) # load the bytes in the context as base64
    image = image.decode('utf8')
    return {'image':image}
Darcy
  • 575
  • 2
  • 8
  • 21

1 Answers1

3

You are calling the get_histogram in wrong way. You can do it like this:

class SearchResultsView(DetailsView):
    ...

    def get_context_data(self, **kwargs):
         context = super(SearchResultsView, self).get_context_data(**kwargs)
         context.update(self.get_histogram(self.request))
         return context

You don't need to call the get_histogram method in get, or override the get method.

Update

I have tried like this:

 class SearchResultsView(DetailsView):
     ...

     def get_histogram(self):
         x = 2
         y = 3
         z = 2
         t= 3    
         plt.plot(x, y)
         plt.plot(z, t)
         plt.show()
         img_in_memory = io.BytesIO()  # for Python 3
         plt.savefig(img_in_memory, format="png")
         image = base64.b64encode(img_in_memory.getvalue())
         return {'image':image}

     def get_context_data(self, *args, **kwargs):
         context = super(SearchResultsView, self).get_context_data(*args, **kwargs)
         context.update(self.get_histogram())
         return context

Output looks like this: enter image description here

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • Thanks @ruddra, I've amended `get` as advised. However, `results.html` is still rendering with a broken image tag where I expect the histogram to be! – Darcy Nov 04 '18 at 03:03
  • @ycrad I have added how I have written the code. Please have a look. – ruddra Nov 04 '18 at 03:47
  • Unfortunately it's still not working; a broken HTML image is still loading. As it works for the example you posted, I now know that my mistake must be somewhere else in the code. If it's of any assistance, I included a link to the repo in the query above. – Darcy Nov 08 '18 at 06:17
  • 1
    Sorry, I might not be able to help you with your repo, but I might help you to debug the issue. In the templates, just put `{{image}}` in somewhere, without the tags and check if there is anything coming through context. If you get nothing, in that case, there could be issue with image generation. – ruddra Nov 08 '18 at 06:35
  • If I do as you suggest, an encoded base64 string is returned. – Darcy Nov 08 '18 at 07:06
  • 1
    That means, you are being able send data from view to template, but the problem resides elsewhere, maybe in plotting. – ruddra Nov 08 '18 at 07:10
  • 1
    Thanks @ruddra, I don't believe that it is in the plotting, as I've used your plotting example, and it still doesn't work. Instead, I now suspect that the issue may be caused by the fact that 'styles.css' isn't loading properly. Anyway, I'm sure it's outside the ambit of the original query, so have marked your answer as accepted. – Darcy Nov 09 '18 at 01:11
  • 1
    Just to let you know, I included the solution in the query @ruddra, thanks again :) – Darcy Nov 09 '18 at 09:55