The (relatively) new Dash library by Plotly offers a way to create feature-rich data visualization and analysis tools completely in Python. This is useful for those of us who prefer the Python language, and and would like to create visualizations without having to switch languages, to, say, Javascript. However, as a newer library, Dash currently offers no official way to incorporate it's projects into Django. Obviously, the ultimate solution for Django developers would be the ability to create a Dash app and incorporate it into a Django project with a simple template tag (which I’m not even sure is possible). The purpose of this blog entry is to document what I've learned about Dash and it's incorporation into Django using the tools currently available to us, in the few days since I've discovered it.
The best method I was able to find to achieve this goal was created by the user ead on the plotly forums at:
https://community.plot.ly/t/embed-dash-plot-into-web-page/5337/3
Check out the repository for the project at:
https://bitbucket.org/m_c_/sample-dash
For my purposes, I've tweaked the code slightly to work with Django 2.0 since that is what I'm running on this server, and also Python 3.6. The solution is actually quite simple and elegant for a work-around.
In order to resolve Dash urls through Django we need two entries to our urlpatterns in urls.py. Note I am using the application name chart for my Dash app:
from chart import views as chartviews
urlpatterns=[
path('dash-', chartviews.dash),
path('_dash-', chartviews.dash_ajax),
]
Next, let's take a look at the views.py. Essentially, all we are doing is handing off the request to the dispatcher function from the as_dash.py file:
from chart.as_dash import dispatcher
def dash(request, **kwargs):
''' '''
return HttpResponse(dispatcher(request))
@csrf_exempt
def dash_ajax(request,**kwargs):
''' '''
return HttpResponse(dispatcher(request), content_type='application/json')
Now for the Dash project itself. I used the original project from the sample-dash repository, but also, as an exercise to help me learn Dash, I recreated the simple demonstration project coded by Chris Parmer (chriddyp) at Plotcon 2016 in this video:
Please note that the code used in the video is no longer working because it was Dash pre-release.
In order to make this work, you can use syntax similar to my as-dash.py (where, for our purposes, the entirety of the Dash project is defined). Also, note that we can think of our functions in this file as being akin to views in Django and each of these "views" for Dash must start with "dash_" so that it can be recognized and loaded by the dispatcher (here, we use dash_index, dash_fig1, dash_fig2, and dash_stockchart). The URLs are the same but with the “_” replaced with “-“.
import sys
from random import randint
import dash
import dash_core_components as dcc
import dash_html_components as dhc
import pandas_datareader as pdr
def dispatcher(request):
'''
Main function
@param request: Request object
'''
app = create_app()
params = {
'data': request.body,
'method': request.method,
'content_type': request.content_type
}
with app.server.test_request_context(request.path, **params):
app.server.preprocess_request()
try;
response = app.server.full_dispatch_request()
except Exception as e:
response = app.server.make_response(app.server.handle_exception(e))
return response.get_data()
def _create_app():
'''Creates dash application'''
app = dash.Dash(csrf_protect=False)
app.config['suppress_callback_exceptions']=True
app.layout = dhc.Div(children=[
dcc.Location(id='url', refresh=False),
dhc.A('Home', href='/dash-index'),
',',
dhc.A('Figure 1', href='/dash-fig1'),
',',
dhc.A('Figure 2', href='/dash-fig2'),
',',
dhc.A('Stock Chart', href='/dash-stockchart'),
dhc.Br(),
dhc.Br(),
dhc.Div(id='content')
])
@app.callback(
dash.dependencies.Output('my-graph', 'figure'),
[dash.dependencies.Input('my-input', 'value')]
)
def update_graph(value):
df = pdr.get_data_yahoo(value)
figure = {
'data': [{
'name': 'Some name',
'mode': 'scatter',
'line': {
'color': 'rgb(0,0,255)',
'opacity': 1
},
'type': 'scatter',
'x': df.index,
'y': df.Open,
}],
'layout;: {
'autosize': True,
'scene': {
'bgcolor': 'rgb(0,0,0)'},
'xaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'X-AXIS',
'color': 'rgb(0,0,0)'
},
'yaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'Y-AXIS',
'color': 'rgb(0,0,0)'
}
}
}
}
return figure
@app.callback(
dash.dependencies.Output('content', 'children'),
[dash.dependencies.Input('url', 'pathname')]
)
def display_page(pathname):
''' '''
if not pathname:
return ''
if pathname =='/':
return dash_index()
method = pathname[1:].replace('-', '_')
func = getattr(sys.modules[__name__], method, None)
if func:
return func()
return app
def dash_index():
''' '''
return 'Dash Home Page'
def dash_fig1():
''' '''
return dcc.Graph(
id='main-graph',
figure={
'data': [{
'name': 'Some name',
'mode': 'scatter',
'line': {
'color': 'rgb(0,0,0)',
'opacity': 1
},
'type': 'bar',
'x': [x for x in range(0, 30)],
'y': [randint(1, 100) for x in range(0, 20)]
}],
'layout': {
'autosize': True,
'width': 800,
'height': 500,
'plot_bgcolor': 'rgb(150,150,150)',
'paper_bgcolor': 'rgb(200,200,200)',
'scene': {
'bgcolor': 'rgb(255,255,255)',
'xaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'X-AXIS',
'color': 'rgb(0,0,0)'
},
'yaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'Y-AXIS',
'color': 'rgb(0,0,0)'
}
}
}
}
)
def dash_fig2():
''' '''
return dcc.Graph(
id='main-graph',
figure={
'data': [{
'name': 'Some name',
'mode': 'scatter',
'line'; {
'color': 'rgb(0,0,255)',
'opacity': 1
},
'type': 'scatter',
'x': [x for x in range(0, 20)],
'y': [randint(1, 100) for x in range(0, 20)]
}],
'layout': {
'autosize': True,
'scene': {
'bgcolor': 'rgb(175,175,175)',
'xaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'X-AXIS',
'color': 'rgb(0,0,0)'
},
'yaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'Y-AXIS',
'color': 'rgb(0,0,0)'
}
}
}
}
)
def dash_stockchart():
''' '''
df = pdr.get_data_yahoo('BRK-A')
children = [
dhc.H1('Stock Chart'),
dcc.Dropdown(
id='my-input',
options=[
{'label': 'Berkshire Hathaway A', 'value': 'BRK-A'},
{'label': 'Berkshire Hathaway B', 'value': 'BRK-B'},
{'label': 'Fiat Chrysler', 'value': 'FCAU'},
{'label': 'Ford', 'value': 'F'},
{'label': 'Apple', 'value': 'AAPL'},
{'label': 'Amazon', 'value': 'AMZN'},
{'label': 'Coke', 'value': 'COKE'},
{'label': 'Tesla', 'value': 'TSLA'},
],
value='BRK-A',
),
dcc.Graph(
id='my-graph',
figure={
'data': [{
'name': 'Some name',
'mode': 'scatter',
'line': {
'color': 'rgb(0,0,255)',
'opacity': 1
},
'type': 'scatter',
'x': df.index,
'y': df.Open,
}],
'layout': {
'autosize': True,
'scene': {
'bgcolor': 'rgb(175,175,175)',
'xaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'X-AXIS',
'color': 'rgb(0,0,0)'
},
'yaxis': {
'titlefont': {'color': 'rgb(0,0,0)'},
'title': 'Y-AXIS',
'color': 'rgb(0,0,0)'
}
}
}
}
)
]
return children
if __name__ == '__main__':
app = _create_app()
app.run_server()
This app will use pandas to connect to Yahoo Finance, pull stock data, and plot it on a Plotly line chart. Check out the end result here:
https://tabre.com/dash-stockchart