我正在尝试构建一个简单的 Flask 应用程序,用户可以在其中上传 CSV 文件。通过相同表单的下拉列表,用户应该能够选择一列。现在,我需要根据上传的 CSV 标头填充此下拉列表。查看了各种解决方案。尝试了一些被标记为正确的。然而,要么我收到错误“Nonetype object not iterable”,要么列未填充。
我不能使用 json.
根据某人在不同查询中发布的答案,我的代码是 -
在 app.py 中:
@app.route('/upload', methods=['GET', 'POST'])
def upload():
col_names = request.args.get('col_names')
if request.method == 'POST':
file = request.files.get('file')
if file and file.filename.endswith('.csv'):
df = pd.read_csv(file)
col1 = request.form.get('col1')
col2 = request.form.get('col2')
col3 = request.form.get('col3')
return render_template('upload.html', col_names=col_names,
col1=col1, col2=col2, col3=col3)
else:
return 'Invalid file format'
else:
return render_template('upload.html', col_names=col_names)
在upload.html中:
<form action="/upload?col_names={{ col_names }}" method="POST" enctype="multipart/form-data">
<label for="file">File:</label>
<input type="file" id="file" name="file"><br />
<label for="col1">Column 1:</label>
<select id="col1" name="col1">
{% for col in col_names %}
<option value="{{ col }}">{{ col }}</option>
{% endfor %}
</select>
<br />
<label for="col2">Column 2:</label>
<select id="col2" name="col2">
{% for col in col_names %}
<option value="{{ col }}">{{ col }}</option>
{% endfor %}
</select>
<br />
<label for="col3">Column 3:</label>
<select id="col3" name="col3">
{% for col in col_names %}
<option value="{{ col }}">{{ col }}</option>
{% endfor %}
</select>
<br />
<button>Upload</button>
</form>
我的建议是选择后立即在后台使用javascript上传文件。现在可以提取文件的列并使用返回的部分提供选择。如果最终发送了表单,您将收到文件和选定的列并可以处理它们。
from flask import (
Flask,
abort,
render_template,
request
)
import pandas as pd
app = Flask(__name__)
@app.route('/csv-upload', methods=['GET', 'POST'])
def csv_upload():
if request.method == 'POST':
file = request.files.get('file')
cols = request.form.getlist('column')
if file and file.filename.endswith('.csv') and len(cols) == 3:
df = pd.read_csv(file)
if len(df.columns) >= 3 and all(c in df.columns and cols.count(c) == 1 for c in cols):
print(df)
print(cols)
return render_template('csv_upload.html')
@app.post('/csv-columns')
def csv_columns():
file = request.files.get('file')
if file and file.filename.endswith('.csv'):
df = pd.read_csv(file)
if len(df.columns) >= 3:
columns = df.columns
return render_template('csv_columns.html', **locals())
abort(400)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Upload</title>
<style type="text/css">
fieldset[disabled] {
display: none;
}
</style>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<fieldset name="file">
<div>
<label for="file">File:</label>
<input type="file" name="file" id="file" accept="text/csv" />
</div>
</fieldset>
<fieldset name="columns" disabled>
</fieldset>
<button type="submit" disabled>Upload</button>
</form>
<script type="text/javascript">
(function() {
const fieldsetElem = document.querySelector('fieldset[name="columns"]');
const fileElem = document.querySelector('input[name="file"]');
const btnElem = document.querySelector('button[type="submit"]');
fileElem.addEventListener('change', async function(event) {
btnElem.disabled = true;
fieldsetElem.disabled = true;
const data = await fetch('/csv-columns', {
method: 'POST',
body: new FormData(this.form)
}).then(resp => resp.ok && resp.text());
if (data) {
fieldsetElem.innerHTML = data;
const selElems = Array.from(fieldsetElem.querySelectorAll('select[name="column"]'));
selElems.forEach(elem => {
elem.addEventListener('change', () => {
btnElem.disabled = !(selElems.every(e => e.value != '')
&& (new Set(Array.from(selElems, e => e.value))).size === selElems.length);
})
});
fieldsetElem.disabled = false;
} else {
console.error('Something went wrong.')
}
});
})();
</script>
</body>
</html>
{% for i in range(3) -%}
<div>
<label for="column-{{i}}">Column {{i+1}}</label>
<select name="column" id="column-{{i}}">
<option value selected>-- Select a column --</option>
{% for col in columns -%}
<option value="{{ col }}">{{ col }}</option>
{% endfor -%}
</select>
</div>
{% endfor -%}