I'm creating a form that allows you to upload a .csv file and then on submit, posts the values inside the csv file to the database.
My problem is that I want to be able to use request.files[] to implement a logic check that only accepts .csv extension files for upload, however, I am not able to use request.files[] when trying to open my csv file with with open(f, 'r') below.
I get this error: "Unexpected type(s): (FileStorage, str) Possible type(s): (Union[str, bytes, PathLike[str], PathLike[bytes], int], str)..."
If I used request.form[], I don't have the error with opening the file, however, then I can't use the "filename" attribute for the logic check as that requires request.files[].
Here is my routes file:
allowed_extensions = ['csv']
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in allowed_extensions
@main.route("/reading_csv", methods=['POST'])
def reading_csv():
if request.method == 'POST':
f = request.files['csvfile']
# logic check
if not allowed_file(f.filename):
flash("Only CSV Files Allowed", 'danger')
return redirect(url_for('main.alloc_summ'))
# opening file, reading it in, and inserting values to database
with open(f, 'r') as csv_file:
csv_reader = csv.DictReader(csv_file, delimiter=',')
all_values = [tuple(line.values()) for line in csv_reader]
con = sql.connect('<hidden path url>')
cur = con.cursor()
cur.executemany('INSERT INTO allocation_summary \
(state, eligible_applicant, recipient, amount, funding_source_id) \
VALUES (?,?,?,?,?)', all_values)
con.commit()
cur.close()
con.close()
flash("Allocations Added Successfully")
return redirect(url_for('main.alloc_summ'))
Here is the HTML:
<td>
<form action="{{url_for('main.reading_csv')}}" method="post" enctype="multipart/form-data">
<input style="margin-bottom: 5px;" type="file" accept=".csv" name="csvfile" value =""> <br>
<input style="margin-bottom: 10px;" type="submit" name="" value="Submit">
</form>
<br>
</td>
I can't open my file using the "f" variable that holds the requested file.
Is there any work around to this? TIA!!
CodePudding user response:
Use .save() method to save your file, don't try to open it
f.save(path: str, filename: str)
Upd:
Read Flask: How to upload file to understand the basic of multipart handling in Flask
CodePudding user response:
FileStorage is a file-like object that understands standard reading functions. It corresponds to a temporary file opened in binary mode. So you don't have to open this file again.
In order to be able to read a CSV file, it should be opened in text mode. This results in a contradiction.
The following example should show you one way to meet your requirements.
import csv, io
allowed_extensions = ['csv']
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in allowed_extensions
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'csvfile' in request.files:
f = request.files['csvfile']
if f.filename != '' and allowed_file(f.filename):
with io.TextIOWrapper(f) as fp:
reader = csv.DictReader(fp, delimiter=',')
values = [tuple(line.values()) for line in reader]
print(values)
# ...
return render_template('upload.html')
<form action="{{url_for('upload')}}" method="post" enctype="multipart/form-data">
<input type="file" accept="text/csv" name="csvfile" />
<input type="submit" />
</form>
Please keep in mind that a file can become very large and contains data that should be validated before it is included in your database. Perhaps saving the file as @sudden_appearance describes is a more appropriate solution.
