I’m trying to find a clean way to add download links to tables I’ve generated. Here’s some sample code (using this post as a baseline example):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pandas as pd
import flask
from StringIO import StringIO
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
from dash.dependencies import Input, Output
DF = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
DFS = {'2002': DF[DF['year'] == 2002],
'2007': DF[DF['year'] == 2007],
}
def tabbed_tables():
table1 = dt.DataTable(rows=DFS['2002'].to_dict('records'),
columns=DFS['2002'].columns
)
button = html.Button('Download', id='download-button-0', value='table-0')
link1 = dcc.Link(button, href='/', id='download-link-0')
table2 = dt.DataTable(rows=DFS['2007'].to_dict('records'),
columns=DFS['2007'].columns
)
button = html.Button('Download', id='download-button-1', value='table-1')
link2 = dcc.Link(button, href='/', id='download-link-1')
tab1 = dcc.Tab(label="2002", children=[table1, link1], id='table-0-tab')
tab2 = dcc.Tab(label="2007", children=[table2, link2], id='table-1-tab')
return dcc.Tabs(id="tabs", children=[tab1, tab2],
style={'fontFamily': 'system-ui'},
content_style={
'borderLeft': '1px solid #d6d6d6',
'borderRight': '1px solid #d6d6d6',
'borderBottom': '1px solid #d6d6d6',
'padding': '44px'
},
)
app = dash.Dash()
app.css.append_css({"external_url":
"https://codepen.io/chriddyp/pen/bWLwgP.css"})
app.layout = html.Div(tabbed_tables(), style={
'width': '100%',
'maxWidth': '1200px',
'margin': '0 auto'
})
@app.callback(
Output('download-link-0', 'href'),
[Input('download-button-0', 'n_clicks')])
def trigger_table_1_download_link(n_clicks):
# Don't trigger when loading page the first time
if n_clicks == "None":
return ''
return '/download?value={}'.format('2002')
@app.callback(
Output('download-link-1', 'href'),
[Input('download-button-1', 'n_clicks')])
def trigger_table_2_download_link(n_clicks):
# Don't trigger when loading page the first time
if n_clicks == "None":
return ''
return '/download?value={}'.format('2007')
@app.server.route('/download')
def download_csv():
value = flask.request.args.get('value')
print("Downloading table ", value)
df = DFS[value]
buff = StringIO()
df.to_csv(buff, encoding='utf-8')
buff.seek(0)
return flask.send_file(buff,
mimetype='text/csv',
attachment_filename=value + '.csv',
as_attachment=True)
if __name__ == '__main__':
app.run_server(debug=True)
The one piece of functionality I’m missing is that It doesn’t download a file. When I click the button on the first tab, I see /download?value=2002
appended to the url, but it doesn’t actually follow through. I have to click on my address bar and hit enter to follow the link and download the file. I’ve tried adding dcc.Location(id='url', refresh=True)
to my layout, but that doesn’t trigger the download. Any ideas?
There’s also a few cleanliness issues I have with this approach:
- Ideally, there should only be one callback defined, but I’d have to be able to parametrize the
component_id
s of theInput
s andOutput
s. Is that possible? I remember reading that callbacks can’t be generated at runtime. - It’d be nice to not have to generate a URL and go through a route to download a file. Is that possible?