Allowing users to download CSV on click

Another option would be to use the Download component from dash-extensions. Here is a small a data frame example,

import dash
import pandas as pd
import dash_html_components as html

from dash.dependencies import Output, Input
from dash_extensions import Download
from dash_extensions.snippets import send_data_frame

# Example data.
df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [2, 1, 5, 6], 'c': ['x', 'x', 'y', 'y']})
# Create app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download", id="btn"), Download(id="download")])

@app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def func(n_nlicks):
    return send_data_frame(df.to_csv, "mydf.csv", index=False)

if __name__ == '__main__':
    app.run_server()
3 Likes

This @app.server.route(ā€™/dash/urlToDownloadā€™) is in a different url. To access it, just add to the end of the url. For example: http://127.0.0.1:8000/dash/urlToDownload.

So, if you want to incorporate it with the app, you can make a button to go to that url.

I got it to work (sort of) with multiple arguments: I can download the correct csv file, and all looks good, but with debug=True it prints an error:

Callback failed: the server did not respond.

Still, it works as intended. What could this error mean?

html.A(dbc.DropdownMenuItem('Button', id='downloadButton'), id='downloadLink'),
ā€¦

@app.callback(
    Output('downloadLink', 'href'),
    [Input('downloadButton', 'n_clicks'),
     Input('variable_selector', 'value'),
     Input('measure_selector', 'value'),
     Input('gender_selector', 'value'),
     Input('age_selector', 'value'),
     Input('immdes_selector', 'value'),
     Input('education_selector', 'value'),
     Input('labor_selector', 'value'),
     Input('year_selector', 'value')]
)
def link_download_map(click, selected_variable, selected_measure, selected_gender, selected_age, selected_heritage, selected_education, selected_labor, selected_year):
    query_params = {'variable': selected_variable,
                    'measure': selected_measure,
                    'gender': selected_gender,
                    'age': selected_age,
                    'heritage': selected_heritage,
                    'education': selected_education,
                    'labor': selected_labor,
                    'year': selected_year
                    }
    query_string = urllib.parse.urlencode(query_params, doseq=True)
    return f"/download?{query_string}" 

@app.server.route('/download') 
def download_csv():
    variable = flask.request.args.get('variable')
    measure = flask.request.args.get('measure')
    gender = flask.request.args.get('gender')
    age = flask.request.args.get('age')
    heritage = flask.request.args.get('heritage')
    education = flask.request.args.get('education')
    labor = flask.request.args.get('labor')
    year = flask.request.args.get('year')
    df_out = df[(df['variable'] == variable) & (df['year'] == int(year)) & (df['gender'] == gender) & (df['age'] == age) & (df['immdes'] == immdes) & (df['education'] == education) & (df['labor'] == labor)].copy()

    str_io = io.StringIO()
    df_out.to_csv(str_io, index=False)

    mem = io.BytesIO()
    mem.write(str_io.getvalue().encode('utf-8'))
    mem.seek(0)
    str_io.close()
    return flask.send_file(mem,
                           mimetype='text/csv',
                           attachment_filename='dataDownload.csv',
                           as_attachment=True,
                           cache_timeout=0
    )

EDIT: the error occurs with firefox, but neither chrome nor edge

As of Dash 1.20.0, we have an official dcc.Download component :tada:

This component is used just like any other Dash component :slightly_smiling_face: - Update the componentā€™s data property with an Output and the file will be downloaded on the userā€™s browser.

This is a much nicer alternative than Flaskā€™s app.server.route as it is easier to make dynamic (no query strings or flask.request.args.get!)

Thank you @Emil for contributing this component to dcc :trophy:

3 Likes