How to display csv data in tabular form in Flask Python? - python

I'm making a web app using the Flask framework with python, I want to make the web able to upload csv without saving and displaying data in a table with the template I made, I've added the syntax for uploading and processing the data until it's in a table view, but after running the website it goes to the 404 not found error message, how can I fix it?
I've made a code in main.py
from datetime import datetime
from flask import Flask, render_template, request, session
from FlaskWebProject2 import app
import os
import pandas as pd
from werkzeug.utils import secure_filename
#*** Flask configuration
UPLOAD_FOLDER = os.path.join('staticFiles', 'uploads')
ALLOWED_EXTENSIONS = {'csv'}
app = Flask(__name__, template_folder='templateFiles', static_folder='staticFiles')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = 'This is your secret key to utilize session in Flask'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['POST', 'GET'])
def uploadFile():
if request.method == 'POST':
# upload file flask
uploaded_df = request.files['uploaded-file']
# Extracting uploaded data file name
data_filename = secure_filename(uploaded_df.filename)
# flask upload file to database (defined uploaded folder in static path)
uploaded_df.save(os.path.join(app.config['UPLOAD_FOLDER'], data_filename))
# Storing uploaded file path in flask session
session['uploaded_data_file_path'] = os.path.join(app.config['UPLOAD_FOLDER'], data_filename)
return render_template('index_upload_and_show_data_page2.html')
#app.route('/show_data')
def showData():
# Retrieving uploaded file path from session
data_file_path = session.get('uploaded_data_file_path', None)
# read csv file in python flask (reading uploaded csv file from uploaded server location)
uploaded_df = pd.read_csv(data_file_path)
# pandas dataframe to html table flask
uploaded_df_html = uploaded_df.to_html()
return render_template('show_csv_data.html', data_var = uploaded_df_html)
I want the website can show the home page and upload, read and show csv data works properly

If you are requesting /upload and getting a 404. This is natural. You have added a handler for /upload endpoint:
#app.route('/upload', methods=['POST', 'GET'])
def uploadFile():
...
But uploadFile only supports POST requests. When you enter localhost/upload into the browser, the browser sends a GET request to the web server. There are two solutions for you with the same result:
Add another if to uploadFile function and check if incoming request is GET and if it is, show a page(e.g. a form to upload file).
Write another function like upload_file_form exclusively for GET requests to show a form.
P.S.: per PEP8, you should use camel_case for function names. e.g. upload_file rather than uploadFile

Related

Having trouble with jsonify in Flask

I am fairly new to Flask and am currently working on a project whose goal is to transcribe mp3 files into JSON. I decided to attempt to use Flask, but it's been more challenging than I thought.
As of right now, I am able to display a example JSON file in one of my html pages, but I have not been able to format it. I looked at some previous answers that told me to use jsonify, but it hasn't worked apparently. If you guys could give me a hand, any kind of comment would be really apreciated. Here is my code:
from flask import Flask, render_template, url_for, request, redirect, json, jsonify
import json
import os
from pathlib import Path
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['POST'])
def upload():
file = request.files['inputFile']
if Path(file.filename).suffix == '.mp3':
filename = os.path.join(app.static_folder, 'data', 'json_test.json')
with open(filename) as json_test:
data = json.load(json_test)
return render_template('index2.html', data=data)
else:
return render_template('erro.html')
if __name__ == "__main__":
app.run(debug=True)

Uploading images to a Flask server using FilePond (React component)

I have a locally hosted Flask server and a small React application. I'm trying to use FilePond as an easy solution for image uploading. FilePond takes care of sending each image to the server.
So the problem I'm obviously having is with the backend code. I've set up my server like this, per Flask's docs
UPLOAD_FOLDER='/images'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_file():
print(request.files)
if request.method == "POST":
# check if the post request has the file part
if 'file' not in request.files:
print('No file part')
return make_response("No File Part", 400)
file = request.files["file"]
# if user does not select file, browser also submit an empty part
# without filename
if file.filename == '':
print('No selected file')
return make_response("No Selected File", 400)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename) # filenames can be dangerous!
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return make_response("Success", 201)
However, when put a
print(request.files)
statement at the beginning of the upload_file() function, it seems that this is the request the server is receiving:
ImmutableMultiDict([('images', <FileStorage: 'imageName.jpg' ('image/jpeg')>)])
and I have no idea how to deal with that. I don't know if 'images' is where 'file' is supposed to be. I'm just not really sure where to go from here or what to do with the data being received. I don't know what is, I've never seen that <> syntax being used.
Can anyone help?
Here's a pastebin for the whole server-side code
Thank you!
Here's a proof of concept app that lets you upload multiple images using a form:
Note the enctype="multipart/form-data" attribute of the form, without this you cannot upload files.
Also note the multiple attribute of the file input. This allows client to select multiple files. You need to use request.files.getlist() to obtain the list of all uploaded files.
Once you generate the path to save the file, saving a werkzeug.FileStorage object is is just calling its .save(path_to_save) method.
from flask import Flask, request, render_template_string, redirect, abort
from werkzeug import secure_filename
from pathlib import Path
UPLOAD_DIR: Path = Path(__file__).parent / 'uploads'
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
app = Flask(__name__)
def is_valid_upload(upload) -> bool:
# some validation logic
return Path(upload.filename).suffix.lower() in ['.jpg', '.jpeg']
#app.route('/', methods=['GET', 'POST'])
def upload():
html = '''
<form action='/' method='POST' enctype="multipart/form-data">
<input type="file" name='images' multiple>
<button>Upload</button>
</form>
'''
if request.method == 'GET':
return html
uploaded_files = request.files.getlist('images')
if not uploaded_files or not uploaded_files[0].filename:
return redirect('/')
valid_uploads = list(filter(is_valid_upload, uploaded_files))
if not valid_uploads:
return 'invalid image(s)', 400
for upload in valid_uploads:
filename = secure_filename(upload.filename)
save_path = str(UPLOAD_DIR / filename)
upload.save(save_path)
return 'uploaded'
if __name__ == "__main__":
app.run()

How to make a dataframe global inside a function and use it in another function in python flask

I am creating a Dataframe by taking input file from user on a website and processing it.After that I want the user to download the final result in a csv file.For that a Dataframe is required from previous function.
I have tried passing the dataframe but it is giving me error as it is defined in another function.
My code is
from flask import Flask, render_template, request, redirect
from werkzeug import secure_filename
app = Flask(__name__)
#app.route('/uploader', methods = ['GET','POST'])
def upload():
new=nrecs[['UserID','ProductID','Rating']]
new['Recommendations'] = list(zip(new.ProductID, new.Rating))
res=new[['UserID','Recommendations']]
res_new=res['Recommendations'].groupby([res.UserID]).apply(list).reset_index()
pd.options.display.max_colwidth = 500
return render_template('simple.html', tables=[res_new.to_html(classes='data')], titles='')
#app.route('/download-csv', methods = ['GET'])
def download():
return res_new.to_csv('Recommendations.csv')
This is a small snipet of my code not the full code.
When a user will click on download recommendations button it should download the csv file.
Is there any other way around it can be done.
You can also store the file on the server and send it to the user in your download-csv route. Here is a send file tutorial
from flask import Flask, render_template, send_file
app = Flask(__name__)
#app.route('/uploader', methods = ['GET','POST'])
def upload():
new=nrecs[['UserID','ProductID','Rating']]
new['Recommendations'] = list(zip(new.ProductID, new.Rating))
res=new[['UserID','Recommendations']]
res_new=res['Recommendations'].groupby([res.UserID]).apply(list).reset_index()
# store the dataframe on the server.
res_new.to_csv('Recommendations.csv')
pd.options.display.max_colwidth = 500
return render_template('simple.html', tables=[res_new.to_html(classes='data')], titles='')
#app.route('/download-csv', methods = ['GET'])
def download():
# return the CSV file to the user here.
return send_file('Recommendations.csv')
You can try using a session object. See this question/answer. However, depending on the size of the dataframe, and what you are ultimately trying to do, this may not be the best way to do this. If you are trying to set up upload/download routes, storing the file on the server/elsewhere and then sending it to the user when they request it may be a better solution.
from flask import Flask, render_template, session
app = Flask(__name__)
# secret key is needed for session
app.secret_key = 'your secret key'
#app.route('/uploader', methods = ['GET','POST'])
def upload():
new=nrecs[['UserID','ProductID','Rating']]
new['Recommendations'] = list(zip(new.ProductID, new.Rating))
res=new[['UserID','Recommendations']]
res_new=res['Recommendations'].groupby([res.UserID]).apply(list).reset_index()
session['reco_df'] = res_new
pd.options.display.max_colwidth = 500
return render_template('simple.html', tables=[res_new.to_html(classes='data')], titles='')
#app.route('/download-csv', methods = ['GET'])
def download():
return session['reco_df'].to_csv('Recommendations.csv')

Template is not shown on the server

I am very new to Flask and Web development so sorry in advanced if I use incorrect terms.
I am trying to create a webpage using Python and Flask. To do so, I have the following:
from flask import Flask, request, render_template, redirect, url_for
from flask_assets import Environment, Bundle
app = Flask(__name__)
assets = Environment(app)
#app.route("/", methods=["GET", "POST"])
def login():
# Code to do the login
error = None
if request.method == 'POST':
# code checking the passwords. if correct:
return render_template('index.html')
# else:
# error
return render_template('login.html', error = error)
What this snippet of code does is to load the login.html where the user is asked to put an username and password and after checking if they are the ones expected, it loads index.html where the user can upload his or her data. Once the data is submitted a new function is called:
#app.route('/transform', methods=["POST"])
def transform():
f = request.files['data_file']
if not f:
return "No file"
# code
return render_template('message.html')
The problem is that while in local the message.html gets display once transform has finished, in the server it doesn't appear although the function eventually does what it's expected to do. The other two templates are correctly displayed both in local and in the server. Could it be due to be in a different route?
The index.html is defined with action='\transform', in case it may give a hint.
Any idea of why could this be happening?

Flask session don't persist data

I have a Javascript application and a Flask application. When the user send data from Js to Flask, I store it on session and it works fine at a specific route:
#app.route(...)
def user(...):
session['name'] = name
print(session['name']) # Works !
But when I tr to get the values on session from another method / route the session is empty:
#app.route(...)
def current():
print(session.keys(), session.values) # Empty !
I have installed Flask Session and set the config to:
'SECRET_KEY': b'...',
'SESSION_TYPE': 'filesystem', # Memcache, null and redis
'SESSION_PERMANENT': False, # True
And then started the Flask application and it not work. I have also try to set session.modified = True after I add some new value to session and still not work.
I have read lots of threads on Stack Over Flow, Reddit, etc; and nothing worked. Tips please ?
TL;DR, enable CORS and credentials support on the back end, and use credentials in the front end code when issuing requests.
I recently ran into a similar issue where I was developing a front end and a back end in separate apps. I noticed that each time I issued a request from the front end client, it would create a new session for each request, which would rapidly bloat the session storage on the back end and made user tracking difficult if not impossible.
I'm assuming that you're Javascript app and Flask app are running separately (i.e., the javascript is not on a template being served by the Flask app and hence the js requests are coming from a different origin).
Suppose we have a simple app with Flask-Session enabled running on port 5000:
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
#app.route('/foo')
def foo():
return session.sid
#app.route('/bar')
def bar():
return session.sid
Now if we run the app if we navigate to either route on a browser(e.g., http://localhost:5000/foo), we would get the same session id. If you open another tab, open the developer tools and issue the following command in the console, you'd get a cors error:
// Using fetch, you can use jquery or axios
fetch("http://localhost:5000/foo").then(response => {
return response.text()
}).then(data => {
console.log(data)
})
You can fix this easily by installing Flask-CORS and wrapping your app in the CORS class:
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)
#app.route('/foo')
def foo():
return session.sid
#app.route('/bar')
def bar():
return session.sid
Now if you run the javascript fetch function above, it prints out a different session id each time the request is invoked, even for the same route. That's because Flask can't track the session unless you're issuing the requests from the same origin or unless you provide some way for flask to identify the session. You can do this from your JS by allowing credentials to be passed:
fetch("http://localhost:5000/foo",
{ credentials: 'include' }).then(response => {
return response.text()
}).then(data => {
console.log(data)
})
However, you will get another CORS error regarding Access-Control-Allow-Credentials. You can fix this in you're Flask app by import the cross_origin decorator, wrapping your routes in the decorator and passing supports_credentials=True to the decorator. The flask code would look something like this:
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS, cross_origin
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)
#app.route('/foo')
#cross_origin(supports_credentials=True)
def foo():
return session.sid
#app.route('/bar')
#cross_origin(supports_credentials=True)
def bar():
return session.sid
Now flask can track the session by the requester (in this case, the browser running the Javascript app).
I had the same problem using classic post request in html. The session, which was still storing values in previous route, would empty itself after my post request.
I solved this using:
app.config.update(SESSION_COOKIE_SAMESITE="None", SESSION_COOKIE_SECURE=True)
I am sharing this in case others are facing the same issue.

Categories