37

I am trying to embed a plotly pie chart in a Django html template. This works fine when the chart is produced in 'online mode' (i.e. the html snippet is stored on the plotly server) but not in 'offline mode' (i.e. when the html is stored locally). In the latter case, the chart does not appear. I want to be able to store the html on my local server and embed the plots from there.

Here is the bit that works:

import plotly.plotly as py
import plotly.graph_objs as go
labels = [1,2,3,4]
values = [10,20,30,40]
ndata = 100
fig = {
    'data': [{'labels': labels,
          'values': values,
          'type': 'pie',
          'textposition':"none",
          'textinfo':"percent",
          'textfont':{'size':'12'},
          'showlegend':'false'}],
    'layout': {'title': 'Total:'+str(ndata),
           'showlegend':'false',
           'height':'200',
           'width':'200',
           'autosize':'false',
           'margin':{'t':'50','l':'75','r':'0','b':'10'},
           'separators':'.,'}
}
plotly_url = py.plot(fig, filename='myfile', auto_open=False)
pie_url = '<iframe width="200" height="200" frameborder="0" seamless="seamless" scrolling="no" src='+plotly_url+'.embed?width=200&height=200&link=false&showlegend=false></iframe>'

Note that pie_url is passed as a string in the Http render request in Django. The template interprets the string as html using the | safe tag, i.e. {{ pie_url|safe }}.

Here is the bit that doesn't work:

from plotly.offline import download_plotlyjs, plot
import plotly.graph_objs as go
labels = [1,2,3,4]
values = [10,20,30,40]
ndata = 100
fig = {
    'data': [{'labels': labels,
          'values': values,
          'type': 'pie',
          'textposition':"none",
          'textinfo':"percent",
          'textfont':{'size':'12'},
          'showlegend':'false'}],
    'layout': {'title': 'Total:'+str(ndata),
           'showlegend':'false',
           'height':'200',
           'width':'200',
           'autosize':'false',
           'margin':{'t':'50','l':'75','r':'0','b':'10'},
           'separators':'.,'}
}
plotly_url = plot(fig, filename='file:///home/website/pie.html', auto_open=False)
pie_url = '''<iframe width="200" height="200" frameborder="0" seamless="seamless" scrolling="no" src=\"'''+plotly_url+'''.embed?width=200&height=200&link=false&showlegend=false\"></iframe>'''

Any advice would be appreciated.

Hooloovoo
  • 865
  • 2
  • 11
  • 21

3 Answers3

56

Instead of writing the html to a file you can have plotly return the html part of the graph as a string. For example, using a class based TemplateView:

EDIT: Update for using recent (as of 2021/08) versions of plotly. The template does not need any changes.

import plotly.graph_objects as go

class Graph(TemplateView):
    template_name = 'graph.html'

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

        x = [-2,0,4,6,7]
        y = [q**2-q+3 for q in x]
        trace1 = go.Scatter(x=x, y=y, marker={'color': 'red', 'symbol': 104, 'size': "10"},
                            mode="lines",  name='1st Trace')

        layout=go.Layout(title="Meine Daten", xaxis={'title':'x1'}, yaxis={'title':'x2'})
        figure=go.Figure(data=['trace1'],layout=layout)

        context['graph'] = figure.to_html()

        return context

This is the original code:

import plotly.offline as opy
import plotly.graph_objs as go

class Graph(TemplateView):
    template_name = 'graph.html'

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

        x = [-2,0,4,6,7]
        y = [q**2-q+3 for q in x]
        trace1 = go.Scatter(x=x, y=y, marker={'color': 'red', 'symbol': 104, 'size': "10"},
                            mode="lines",  name='1st Trace')

        data=go.Data([trace1])
        layout=go.Layout(title="Meine Daten", xaxis={'title':'x1'}, yaxis={'title':'x2'})
        figure=go.Figure(data=data,layout=layout)
        div = opy.plot(figure, auto_open=False, output_type='div')

        context['graph'] = div

        return context

and the template:

{% if graph %}
<div style="width:600;height:500">
{{ graph|safe }}
</div>
{% endif %}
Christian K.
  • 2,785
  • 18
  • 40
  • 5
    You can probably put this in your views to render this context `g = Graph()` `context = g.get_context_data()` `return render(request, 'app/stats.html', context)` EDIT: use TemplateViews directly in urls https://docs.djangoproject.com/en/1.10/ref/class-based-views/base/#templateview – Naman Apr 18 '17 at 08:38
  • 4
    For all those not familiar with class-based views, to make the code working, remember to add from django.views.generic import TemplateView. Also, read the docs pointed out by Naman – Tms91 May 24 '20 at 10:44
  • Thanks! This is so much easier than using matplotlib. – hehe Jul 24 '22 at 00:46
33

There's been a couple of updates to plotly, some of which are applicable here for plotly 4+:

  • Plotly is offline by default now

    Note: No internet connection, account, or payment is required to use plotly.py. Prior to version 4, this library could operate in either an "online" or "offline" mode. The documentation tended to emphasize the online mode, where graphs get published to the Chart Studio web service. In version 4, all "online" functionality was removed from the plotly package and is now available as the separate, optional, chart-studio package (See below). plotly.py version 4 is "offline" only, and does not include any functionality for uploading figures or data to cloud services.

  • Figures now have a to_html function

This allows you to do the following to embed a graph offline:

graph = fig.to_html(full_html=False, default_height=500, default_width=700)
context = {'graph': graph}
response = render(request, 'graph.html', context)

And in graph.html you can do:

{% if graph %}
{{ graph|safe }}
{% else %}
<p>No graph was provided.</p>
{% endif %}
Ryan Peterson
  • 519
  • 6
  • 7
2

Let's suppose that your piece of code that draws the graph, i.e.

from plotly.offline import download_plotlyjs, plot
import plotly.graph_objs as go
labels = [1,2,3,4]
values = [10,20,30,40]
ndata = 100
fig = {
    'data': [{'labels': labels,
          'values': values,
          'type': 'pie',
          'textposition':"none",
          'textinfo':"percent",
          'textfont':{'size':'12'},
          'showlegend':'false'}],
    'layout': {'title': 'Total:'+str(ndata),
           'showlegend':'false',
           'height':'200',
           'width':'200',
           'autosize':'false',
           'margin':{'t':'50','l':'75','r':'0','b':'10'},
           'separators':'.,'}
}
plotly_url = plot(fig, filename='file:///home/website/pie.html', auto_open=False)
pie_url = '''<iframe width="200" height="200" frameborder="0" seamless="seamless" scrolling="no" src=\"'''+plotly_url+'''.embed?width=200&height=200&link=false&showlegend=false\"></iframe>'''

is a funtion named graph_function(). Inside that one, just substitute

plotly_url = plot(fig, filename='file:///home/website/pie.html', auto_open=False)
pie_url = '''<iframe width="200" height="200" frameborder="0" seamless="seamless" scrolling="no" src=\"'''+plotly_url+'''.embed?width=200&height=200&link=false&showlegend=false\"></iframe>'''

with

plt_div = plotly.offline.plot(fig, output_type='div')

return plt_div

then in your views.py:

def graph_view(request):
    my_graph=graph_function()
    context={'graph':my_graph}
    return render(request, 'my_template.html', context_dict)

and then in your template:

{% if graph %}
{{ graph|safe }}
{% else %}
<p>No graph was provided.</p>
{% endif %}
Tms91
  • 3,456
  • 6
  • 40
  • 74