What I'm trying to do
I'm building a simple single-route Flask app that takes a value from a single-field form, creates multiple CSV files & automatically provides the files after the form is submitted.
Existing, Related Question
I stumbled upon the Download multiple CSVs using Flask? question that contains an answer that explains how to do exactly what I'm looking to do: return multiple downloads.
My Problem
I've implemented the MultipartEncoder
from the requests_toolbelt
as the answer shows, but when submitting the form it just downloads a single file (named after the route) with no extension instead of downloading all the files.
Things I tried to diagnose
If I open up the file in notepad++ I can see that all CSV files are included in the file separated by their Content-Type
& Content-Disposition
headers. So, the data is all present, but for some reason the files are not individually downloaded. That leads me to believe that my form is misconfigured or maybe I need to post to a different route.
What am I doing wrong? How can I accomplish downloading multiple files from a single route?
Minimal Working Example Code
app.py
from flask import Flask, Response, render_template, request
from requests_toolbelt import MultipartEncoder
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'n0T_a-R3a1_sEcR3t-KeY'
Bootstrap(app)
def build_files_for(term):
# Create CSV files based on term
# Return filenames
return ['filename1.csv', 'filename2.csv', 'filename3.csv']
@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
def index():
form = TermBuilderForm()
if form.validate_on_submit():
term_results = form.term.data
downloads = build_files_for(term_results)
me_dict = {}
for i, download in enumerate(downloads, 1):
me_dict['field' + str(i)] = (download, open(download, 'rb'), 'text/csv')
m = MultipartEncoder(me_dict)
return Response(m.to_string(), mimetype=m.content_type)
return render_template('index.html', form=form)
class TermBuilderForm(FlaskForm):
term = StringField('Term', validators=[DataRequired()], id='term')
submit = SubmitField('Create')
if __name__ == '__main__':
app.run(debug = True)
index.html
{% extends 'bootstrap/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}
Term Builder
{% endblock %}
{% block scripts %}
{{ super() }}
{% endblock %}
{% block content %}
<div class="container" style="width:100%;padding-left:35px;">
{% block app_content %}
<h1>Term Builder</h1>
{% if form %}
<!--enctype="multipart/form-data"-->
<form id="termbuilder" action="{{ url_for('index') }}" method="post" style="width:30%">
<div style="display:none">{{ wtf.form_field(form.csrf_token) }}</div>
<div class="row">
{{ wtf.form_field(form.term) }}
</div>
<hr>
<p>{{ wtf.form_field(form.submit) }}</p>
</form>
{% endif %}
{% endblock %}
</div>
{% endblock %}