CONTEXT
I've created a Flask web app that has a form on the webpage, and then I want the user to see the results of that form immediately to the right of the form. When the form is submitted, a python script is activated on the backend, and this produces an HTML result file (authorList.html
) - which is what I want to display next to the form.
PROBLEM
From what I understand, this requires me to use Ajax, so I can get results without refreshing the page, but I'm not as familiar with how to pass that HTML result file from the flask app.py to the HTML template and then append that as a node to the DOM tree.
1) I have my code set up so far such that the upload
route is invoked, which then renders index.html
, and then the JS code in there refers back to the results
route, which then renders index.html
again, passing in the HTML result file as a string. I've partially set up the HTML-string-to-DOM-element conversion step in index.html
under where it says <p>Results should display here.</p>
(as this is the part of the page where I want the HTML result file to display) but am not sure if I'm on the right track and how to proceed with doing appendchild.
2) Also, when I try running this code below the way it is, I get a JS error of Uncaught SyntaxError: Unexpected token ;
pointing to this line of index.html
: var d = document.createElement('div'); d.innerHTML = ; return d.firstChild;
...is it because I'm not passing in the data
variable correctly on the Flask app side? (RESOLVED AND UPDATED IN CODE ACCORDINGLY)
(note: I am not familiar with JS, so if this seems simple, I apologize in advance!)
SOURCE CODE
app.py:
@app.route("/", methods=['GET', 'POST'])
def upload():
return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])
@app.route("/results", methods=['POST'])
def results():
data = []
if request.method == 'POST':
if request.form['affiliation'] == "letter":
affiliation = "let"
elif request.form['affiliation'] == "number":
affiliation = "num"
proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
while proc.poll() is None:
time.sleep(0.5)
# Convert resulting HTML file to a string and pass to index.html
with open('authorList.html') as f:
data.append("".join(f.readlines()))
return render_template('index.html', data=''.join(data))
index.html:
<html>
<head>
<script src=".9.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('form').submit(function (e) {
var url = "{{ url_for('results') }}";
$.ajax({
type: "POST",
url: url,
data: $('form').serialize(),
success: function (data) {
console.log(data)
}
});
e.preventDefault();
});
});
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-6">
<div>
<br>
<p>Download the template file below and re-upload with your custom author information:</p>
<a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
<form action="" id="myform" method=post enctype=multipart/form-data>
<div id="buttonDiv">
<p><input type=file name=file value="Choose File">
<p>Mark affiliations with:</p>
<input type="radio" name="affiliation" value="number" id="number" class="form-radio" checked><label for="number">Number</label><br>
<input type="radio" name="affiliation" value="letter" id="letter" class="form-radio"><label for="letter">Letter</label>
<br><br>
</div>
<input type=submit value=Upload></p>
</form>
</div>
</div>
<div class="col-sm-6">
<div>
<p>Results should display here.</p>
<script>
var d = document.createElement('div'); d.innerHTML = "{{ data }}"; return d.firstChild;
# Need code for appending child
</script>
</div>
</div>
</div>
</div>
</body>
</html>
UPDATE
I tried the following change in my JS code (in index.html
) but am still not seeing any results appear on the homepage.
<script>
var data
$(document).ready(function() {
$('form').submit(function (e) {
var url = "{{ url_for('results') }}"; // send the form data here.
$.ajax({
type: "POST",
url: url,
data: $('form').serialize(),
success: function (data) {
var d = document.createElement('div');
d.innerHTML = data;
$(".my-results").html(data);
}
});
e.preventDefault(); // block the traditional submission of the form.
});
});
</script>
.
.
.
.
<div>
<br>
<p class="my-results">Results should display here.</p>
</div>
</div>
UPDATE 2: full app.py
@app.route("/", methods=['GET', 'POST'])
def upload():
return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])
@app.route("/results", methods=['GET', 'POST'])
def results():
if 'file' not in request.files:
flash('No file chosen', 'danger')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file', 'danger')
return redirect(request.url)
filename = secure_filename(file.filename)
if not allowed_file(file.filename):
flash('Incorrect file extension. Must be .TXT!', 'danger')
if places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)) == False:
flash('There is an affiliation missing from your Place list. Please re-try.', 'danger')
return redirect(request.url)
else:
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
os.chdir('/Users/cdastmalchi/Desktop/author_script/')
if request.form['affiliation'] == "letter":
affiliation = "let"
elif request.form['affiliation'] == "number":
affiliation = "num"
if "Yes sep period" in request.form.getlist('period'):
period = "y"
else:
period = "n"
if "Yes sep space" in request.form.getlist('space'):
space = "y"
else:
space = "n"
proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
# Wait until process terminates
while proc.poll() is None:
time.sleep(0.5)
with open("authorList.html") as f:
data = ''.join(f.readlines())
print(data)
return data
CONTEXT
I've created a Flask web app that has a form on the webpage, and then I want the user to see the results of that form immediately to the right of the form. When the form is submitted, a python script is activated on the backend, and this produces an HTML result file (authorList.html
) - which is what I want to display next to the form.
PROBLEM
From what I understand, this requires me to use Ajax, so I can get results without refreshing the page, but I'm not as familiar with how to pass that HTML result file from the flask app.py to the HTML template and then append that as a node to the DOM tree.
1) I have my code set up so far such that the upload
route is invoked, which then renders index.html
, and then the JS code in there refers back to the results
route, which then renders index.html
again, passing in the HTML result file as a string. I've partially set up the HTML-string-to-DOM-element conversion step in index.html
under where it says <p>Results should display here.</p>
(as this is the part of the page where I want the HTML result file to display) but am not sure if I'm on the right track and how to proceed with doing appendchild.
2) Also, when I try running this code below the way it is, I get a JS error of Uncaught SyntaxError: Unexpected token ;
pointing to this line of index.html
: var d = document.createElement('div'); d.innerHTML = ; return d.firstChild;
...is it because I'm not passing in the data
variable correctly on the Flask app side? (RESOLVED AND UPDATED IN CODE ACCORDINGLY)
(note: I am not familiar with JS, so if this seems simple, I apologize in advance!)
SOURCE CODE
app.py:
@app.route("/", methods=['GET', 'POST'])
def upload():
return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])
@app.route("/results", methods=['POST'])
def results():
data = []
if request.method == 'POST':
if request.form['affiliation'] == "letter":
affiliation = "let"
elif request.form['affiliation'] == "number":
affiliation = "num"
proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
while proc.poll() is None:
time.sleep(0.5)
# Convert resulting HTML file to a string and pass to index.html
with open('authorList.html') as f:
data.append("".join(f.readlines()))
return render_template('index.html', data=''.join(data))
index.html:
<html>
<head>
<script src="http://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('form').submit(function (e) {
var url = "{{ url_for('results') }}";
$.ajax({
type: "POST",
url: url,
data: $('form').serialize(),
success: function (data) {
console.log(data)
}
});
e.preventDefault();
});
});
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-6">
<div>
<br>
<p>Download the template file below and re-upload with your custom author information:</p>
<a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
<form action="" id="myform" method=post enctype=multipart/form-data>
<div id="buttonDiv">
<p><input type=file name=file value="Choose File">
<p>Mark affiliations with:</p>
<input type="radio" name="affiliation" value="number" id="number" class="form-radio" checked><label for="number">Number</label><br>
<input type="radio" name="affiliation" value="letter" id="letter" class="form-radio"><label for="letter">Letter</label>
<br><br>
</div>
<input type=submit value=Upload></p>
</form>
</div>
</div>
<div class="col-sm-6">
<div>
<p>Results should display here.</p>
<script>
var d = document.createElement('div'); d.innerHTML = "{{ data }}"; return d.firstChild;
# Need code for appending child
</script>
</div>
</div>
</div>
</div>
</body>
</html>
UPDATE
I tried the following change in my JS code (in index.html
) but am still not seeing any results appear on the homepage.
<script>
var data
$(document).ready(function() {
$('form').submit(function (e) {
var url = "{{ url_for('results') }}"; // send the form data here.
$.ajax({
type: "POST",
url: url,
data: $('form').serialize(),
success: function (data) {
var d = document.createElement('div');
d.innerHTML = data;
$(".my-results").html(data);
}
});
e.preventDefault(); // block the traditional submission of the form.
});
});
</script>
.
.
.
.
<div>
<br>
<p class="my-results">Results should display here.</p>
</div>
</div>
UPDATE 2: full app.py
@app.route("/", methods=['GET', 'POST'])
def upload():
return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])
@app.route("/results", methods=['GET', 'POST'])
def results():
if 'file' not in request.files:
flash('No file chosen', 'danger')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file', 'danger')
return redirect(request.url)
filename = secure_filename(file.filename)
if not allowed_file(file.filename):
flash('Incorrect file extension. Must be .TXT!', 'danger')
if places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)) == False:
flash('There is an affiliation missing from your Place list. Please re-try.', 'danger')
return redirect(request.url)
else:
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
os.chdir('/Users/cdastmalchi/Desktop/author_script/')
if request.form['affiliation'] == "letter":
affiliation = "let"
elif request.form['affiliation'] == "number":
affiliation = "num"
if "Yes sep period" in request.form.getlist('period'):
period = "y"
else:
period = "n"
if "Yes sep space" in request.form.getlist('space'):
space = "y"
else:
space = "n"
proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
# Wait until process terminates
while proc.poll() is None:
time.sleep(0.5)
with open("authorList.html") as f:
data = ''.join(f.readlines())
print(data)
return data
Share
Improve this question
edited Apr 26, 2019 at 15:51
claudiadast
asked Apr 23, 2019 at 20:24
claudiadastclaudiadast
6093 gold badges13 silver badges34 bronze badges
4
-
The trouble is that the returned value,
data
, in thesuccess
handler of the$.ajax
request is actually your entireindex.html
file again. Isdata
in theajax
request scope supposed to be storing the value of''.join(data)
, created on the Python side? – Ajax1234 Commented Apr 25, 2019 at 23:10 -
@Ajax1234, yes so how I'd like it is to have
success
in the$.ajax
return the value of' '.join(data)
from the python side (which is the form's html result that I want to display on the same page). – claudiadast Commented Apr 25, 2019 at 23:16 -
Ok, just one more thing:
file
is not defined inapp.py
, can you clarify? – Ajax1234 Commented Apr 25, 2019 at 23:34 -
Ah yes, sorry that is in my
results
route, for which I did not include the full function initially, but it's all there now in Update 2! – claudiadast Commented Apr 26, 2019 at 15:53
2 Answers
Reset to default 2To upload a file dynamically, you need to use the FormData
object in Javascript with a POST
request. This solution sends two separate requests: the POST
request with the file data, and a GET
request with the additional values. The filename is stored in flask.session
to be used in the context of the GET
request when the final data is puted:
First, in your app.py
, you need three routes: a route to render index.html
, a route to handle the file data, and lastly, a route to return the html:
app.py
:
import flask, string, random
import json
app = flask.Flask(__name__)
app.secret_key = ''.join(random.choice(string.ascii_letters) for _ in range(20)) #needed to use flask.session
@app.route('/', methods=['GET'])
def home():
return flask.render_template('index.html')
@app.route('/process_file', methods=['POST'])
def process_file():
#here, you can run all the checks as before, but instead of flash, you can return jsonified results to read in the front-end
if 'file' not in flask.request.files or not flask.request.files['file'].filename:
return flask.jsonify({'result':'False', 'message':'no files selected'})
file = flask.request.files['file']
filename = secure_filename(file.filename)
if not allowed_file(file.filename):
return flask.jsonify({'result':'False', 'message':'Must be TXT file!'})
if not places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)):
return flask.jsonify({'result':'False', 'message':'There is an affiliation missing from your Place list. Please re-try.'})
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flask.session['filename'] = filename
return flask.jsonify({'result':'True'})
@app.route('/upload_vals')
def upload_vals():
payload = json.loads(flask.request.args.get('payload'))
#do something with payload
#payload will now be in the form:
#{'affiliation':'Number', 'period':'no', 'space':'yes'}
proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(flask.session['filename'], 'y' if _checked['period'] else 'n', 'y' if _checked['space'] else 'n', aff[:3]), shell=True, stdout=subprocess.PIPE)
while proc.poll() is None:
time.sleep(0.5)
with open("authorList.html") as f:
data = ''.join(f.readlines())
return flask.jsonify({'data':data})
index.html
:
<html>
<head>
<script src="http://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
</body>
<div class='wrapper'>
<p>Download the template file below and re-upload with your custom author information:</p>
<a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
<form action="" id="myform" method=post enctype=multipart/form-data>
<input type=file name=file value="Choose File">
<p class='error_message'></p>
</form>
<div id="buttonDiv">
<p>Mark affiliations with:</p>
<input type="radio" name="affiliation" value="number" data-key='affiliation' data-value='number' class="form-radio main_checkbox" checked><label for="number">Number</label><br>
<input type="radio" name="affiliation" value="letter" data-key='affiliation' data-value='letter' class="form-radio main_checkbox"><label for="letter">Letter</label><br>
<p>Separate initials with period:</p>
<input type="radio" name="period" value="separated" data-key='period' data-value='yes' class="form-radio period"><label for="period">Yes</label><br>
<input type="radio" name="period" data-key='period' data-value='no' value="separated" class="form-radio period" checked><label for="period">No</label>
<br>
<p>Separate initials with space:</p>
<input type="radio" name="space" value="separated" data-key='space' data-value='yes' class="form-radio spacing"><label for="space">Yes</label><br>
<input type="radio" name="space" data-key='space' data-value='no' value="separated" class="form-radio spacing" checked><label for="space">No</label><br>
<br><br>
</div>
<button class='submit_data'>Submit</button>
<div>
<br>
<p class="my-results"></p>
</div>
</div>
<script>
$(document).ready(function(){
$('.wrapper').on('click', '.submit_data', function(){
var form_data = new FormData($('#myform')[0]);
var flag = true;
$.ajax({
type: 'POST',
url: '/process_file',
data: form_data,
contentType: false,
cache: false,
processData: false,
success: function(data) {
if (data.result === 'False'){
$('.error_message').html(data.message)
flag = false;
}
},
});
if (flag){
var payload = {};
$('.form-radio').each(function(){
if ($(this).prop('checked')){
payload[$(this).data('key')] = $(this).data('value');
}
});
$.ajax({
url: "/upload_vals",
type: "get",
data: {'payload':JSON.stringify(payload)},
success: function(response) {
$(".my-results").html(response.data);
},
});
}
});
});
</script>
</html>
It is slightly unclear in your updated app.py
where the period
and space
form values originate from in the HTML, however, in the the index.html
above, two additional checkboxes are provided to receive this value from the user.
To fix your syntax error put quotes around {{ data }}
.
d.innerHTML = "{{ data }}";
Without the quotes the result is
d.innerHTML = ;
But don't worry because you need to move that code anyway.
The JavaScript in the second <script>
tag doesn't know about the data
because it is out of scope. You need to move that code into your $.ajax
success method. This should work better:
$.ajax({
type: "POST",
url: url,
data: $('form').serialize(),
success: function (data) {
var d = document.createElement('div');
d.innerHTML = data;
}
});
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745276653a4620082.html
评论列表(0条)