Black Lives Matter. Please consider donating to Black Girls Code today.

How to use Dash form input to invoke download csv streaming through flask route

Hi all,

I am trying to write an app using dash components to generate sql queries, then allow users to stream download queried data in csv format, writing row by row.
Is there anyway to have the dash app to do similar stuff as below?

from flask import Flask, render_template, request, Response
from google.cloud import bigquery

app = Flask(__name__, template_folder = 'template')

#google cloud config
client = bigquery.Client()
job_config = bigquery.QueryJobConfig()
job_config.use_legacy_sql = False


@app.route('/')
def index(): 
	return render_template('index.html')

@app.route('/download.csv', methods = ['GET', 'POST'])
def download_csv():
	query = request.form['query']
	query_job = client.query(
		query,
		location='US',
		job_config=job_config)  
	def generate():
		for row in query_job:
			yield ','.join(str(x) for x in row.values()) + '\n'
	return Response(generate(), mimetype = 'text/csv')

if __name__ == '__main__':
	app.run(debug = True)

above code takes a text input from a html form and the button submit will invoke a response that writes data row by row into a csv file on the user end called download.csv.

Is there anyway to get the text input from a dash component? I tried something as below, but it didn’t work.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import plotly.graph_objs as go
from datetime import datetime as dt
from google.cloud import bigquery
from flask import Flask, render_template, request, Response


client = bigquery.Client()
job_config = bigquery.QueryJobConfig()
job_config.use_legacy_sql = False

app = dash.Dash(__name__)
server = app.server

app.config.supress_callback_exceptions = True

app.layout = html.Div([
    html.Div(dcc.Input(id='input-box', type='text')),
    html.Button('Submit', id='button'),
    html.Div(id='hidden-div', style={'display':'none'})
])


@server.route('/download.csv', methods = ['GET', 'POST'])
@app.callback(
    dash.dependencies.Output('hidden-div', 'children'),
    [dash.dependencies.Input('button', 'n_clicks')],
    [dash.dependencies.State('input-box', 'value')])
def update_output(n_clicks, value):
	query = value
	query_job = client.query(
		query,
		location='US',
		job_config=job_config)  
	def generate():
		for row in query_job:
			yield ','.join(str(x) for x in row.values()) + '\n'
	return Response(generate(), mimetype = 'text/csv')

if __name__ == '__main__':
    app.run_server(debug=True)

The error message was TypeError: Object of type ‘Response’ is not JSON serializable.
it seems like the response gets returned to the hidden-div, but I would like it to be returned to the server route. how do I do that?

I’m pretty sure using this method wouldn’t be working since @app.callback is doing more than just simply returning the 'value' from 'input-box'.

This discussion should give you a workaround to achieve what you’re looking for however: Download raw data