I have a flask application which receives a user/portfolio id form, then creates a page displaying user data, info, graphs, etc. I'm trying to insert a download CSV data link/button for each one of the users portfolios/instruments.
I'm doing it through flask sessions. As the CSVs data exceeds the cookies limit, I need to use special sessions. I've tried with Redis with no success until now (but it may be an option), so I'm using file system Flask session, which is giving an error OSError: [Errno 36] File name too long.
Here's a reduction of my code:
app.py
from flask import Flask, render_template, redirect, request, session, send_file
from flask_wtf import FlaskForm
from flask_session import Session #this module has upper and lower case versions - I'm using lower
from wtforms import StringField, SelectField, SubmitField, validators
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
#import my modules
app = Flask(__name__)
SESSION_TYPE = 'filesystem' #redis #filesystem
app.config.from_object(__name__)
Session(app)
Bootstrap(app)
app.config.from_mapping(SECRET_KEY=.....)
...
#home route
@app.route('/', methods=['GET', 'POST'])
def home():
form = DataForm(request.form)
if request.method == 'POST' and form.validate_on_submit():
data_id = request.form['data_id']
target= request.form['target']
return redirect(f"/data/{target}/{data_id}")
return render_template('home.html', form=form)
#route which receives user/portfolio id, gets data and creates display page
@app.route('/data/<target>/<data_id>')
def customeranalysis(target, data_id):
data_id = procedures.handle_id(data_id)
if target == 'user':
user_infos, user_names, graphs, annual_tables, twelve_months_tables, monthly_tables, csvs = procedures.user_info(data_id)
for i in range(len(user_names)):
session[user_names[i]] = csvs[i]
return render_template('data_display_page.html',
user_id=data_id,
dfs=user_infos,
names=user_names,
graphs=graphs,
annual_tables=annual_tables,
twelve_months_tables=twelve_months_tables,
monthly_tables=monthly_tables)
if target == 'portfolio':
#same as previous if, a little different
#route for the download page, to which the download link in previous route points
@app.route("/get_csv/<filename>", methods=['GET', 'POST'])
def get_csv(filename):
df = session[filename]
try:
return send_file(df,
as_attachment=True,
attachment_filename='dados.csv'
)
except FileNotFoundError:
abort(404)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
So, in my data_display_page.html template page I have the following code, which creates the download link:
data_display_page.html
{% for i in range(names| length) %}
...
<a href="{{url_for('get_csv', filename=names[i])}}" id="download" class="btn btn-outline-info">Download data</a>
...
When I run docker-compose, all runs well, but when I click on the download load link and I'm redirected, I get an Internal Server Error on browser and on console I get the following:
cmd: docker-compose error
web_1 | 172.27.0.1 - - [15/Apr/2021 18:34:15] "GET /data/portfolio/portfolio_api_id HTTP/1.1" 200 -
web_1 | [2021-04-15 18:34:47,836] ERROR in app: Exception on /get_csv/portfolio_name[GET]
web_1 | Traceback (most recent call last):
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
web_1 | response = self.full_dispatch_request()
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
web_1 | rv = self.handle_user_exception(e)
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
web_1 | reraise(exc_type, exc_value, tb)
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
web_1 | raise value
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
web_1 | rv = self.dispatch_request()
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
web_1 | return self.view_functions[rule.endpoint](**req.view_args)
web_1 | File "/code/app.py", line 86, in get_csv
web_1 | return send_file(df,
web_1 | File "/usr/local/lib/python3.8/site-packages/flask/helpers.py", line 629, in send_file
web_1 | file = open(filename, "rb")
web_1 | OSError: [Errno 36] File name too long: #Long string which is actually not the CSV's name, but the CSV string itself
Somehow it thinks the file name is the whole CSV string, with all columns and rows and etc.