Reputation: 3
I know this question has been asked before but I have been through all the posts and none of the solutions seem to work for me.
Please bear with me. I am new to Flask and html and trying to build my first web app.
It's supposed to work as follows: The user uploads an Excel workbook and the workbook headers are displayed in a dropdown list using the html "select" tag. The user should then select one of the headers. I would then like to pass the selected header into a function.
I am able display the workbook headers in the dropdown list, but when I select a header, nothing happens. Does anyone have any idea what I'm doing wrong?
Please see python code below:
import flask
from flask import Flask
from flask import request
import pandas as pd
app = Flask(__name__)
@app.route("/", methods=["GET", "POST"])
def index():
global headers_list
headers_list=[]
if request.method == "POST":
df=request.files["file"]
if df:
df=pd.read_excel(df)
headers_list=get_headers(df)
selected_header = request.form.get("header")
print(str(selected_header)) #to test the code
else:
print("No file selected")
return (flask.render_template("./index.html", headers_list=headers_list))
def get_headers(dataframe):
headers=list(dataframe.columns)
return headers
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8080, debug=True)
HTML below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content=""
<title><Contra Tool</title>
</head>
<body>
<h1>Contra Tool</h1>
<p>Describe functionality</p>
<br/>
</body>
<form action="" method="post" enctype=multipart/form-data>
<label for ="myfile" > Select a file:</label>
<input type="file" id="myfile" name="file" EnableViewState=True>
<input type="submit" value="Upload">
</form>
<br><br>
<div class="dropdown">
<button class="dropbtn">Dropdown</button>
<div>
<form action="" method="POST">
<SELECT class="dropdown-content" name="header" method="POST" action="/">
<ul>
<option value="{{headers_list[0]}}" selected>{{headers_list[0]}}</option>
{% for h in headers_list[1:]%}
<option value="{{h}}">{{h}}</option>
{% endfor %}
</ul>
</SELECT>
</form>
</div>
</div>
<br/>
<input type="submit">
</html>
Upvotes: 0
Views: 2932
Reputation: 8552
Since I am assuming that you do not want to save the excel file on the server, in my opinion there remains a variant in which the file is transferred twice.
If the user selects a file, it is transferred in the background to query the header columns. The select element is filled with the information received.
From now on a column can be selected and the form can be transferred.
In my example there are two routes. One to display and process the form and another which on request returns the header columns in JSON format.
from flask import Flask
from flask import abort, jsonify, render_template, request
import pandas as pd
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
head = request.form.get('head');
print(f'selected "{head}"')
return render_template('index.html')
@app.route('/headers', methods=['POST'])
def headers():
if 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
headers = list(df.columns)
return jsonify(headers=headers)
abort(400)
If the user selects a file, it is sent to the second route via AJAX. The select element is emptied and refilled and all necessary further elements are made available after the response from the server has been received. If the user presses submit, the completed form is sent with the file and the selected column.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<select name="head" id="head" disabled>
<option>Choose Header</option>
</select>
<input type="submit" id="submit" disabled />
</form>
<script type="text/javascript">
(() => {
const fileElem = document.getElementById('file');
fileElem.addEventListener('change', evt => {
const formData = new FormData();
formData.append('file', evt.target.files[0]);
fetch('/headers', { method: 'POST', body: formData })
.then(resp => resp.json())
.then(data => {
// clear select options
const selectElem = document.getElementById('head');
for (let i=selectElem.options.length-1; i >= 0; --i) {
selectElem.remove(i);
}
// populate select options
const headers = data['headers'];
for (const head of headers) {
const optElem = document.createElement('option');
optElem.value = head;
optElem.innerHTML = head;
selectElem.append(optElem);
}
selectElem.disabled = false;
const elem = document.getElementById('submit');
elem.disabled = false;
});
});
})();
</script>
</body>
</html>
Remember, this is a minimal example and may need to be adapted and revised to meet your requirements.
Upvotes: 1