Flask is not re-rendering the template page with the new variable.
Flask code (app.py):
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
#app.route("/",methods = ['GET', 'POST'])
#app.route("/<value>",methods = ['GET', 'POST'])
def index(value=1):
print(value)
return render_template('home.html',number=value)
#app.route("/next",methods = ['GET', 'POST'])
def next():
if request.method == 'POST':
last_num = list(dict(request.form).keys())[0]
last_num = int(last_num)
return redirect(url_for("index",value=last_num+1))
if __name__ == '__main__':
app.run()
HTML page (home.html):
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$(function() {
$('#submit').click(function() {
$.ajax({
url: '/next',
data: String(Number(document.getElementById("number").textContent)),
type: 'POST',
success: function(response) {
console.log("Awesome");
},
error: function(error) {
console.log(error);
}
});
});
});
</script>
</head>
<body>
<div id="number">
{{number}}
</div>
<button type="button" id="submit">Next</button>
</body>
</html>
The (last) number in HTML page nicely reaches the flask code and the index function gets the incremented value also. But the HTML page do not reload with the new number variable.
Couple things.
Firstly, you can interpolate your flask variables directly into your
JavaScript. It may cause errors in your editor (not in this case), but when the page renders, it will work
properly. (ie. data: "{{ number|safe }}")
Secondly, you are not redirecting the client. You are returning a redirect from your API call, but you are not following it. That function only works if you were rendering the /next endpoint as your page. You should instead return the URL you want the client to navigate to. You can set the page URL a number of ways in JavaScript. If you just want to reload the page, put location.reload(true) in your success handler. (by putting forceGet to true, you will prevent the browser for reloading from the cache so the server will actually update the variable on the page). Note that if you do this, you will need to store the variable globally on your backend as it will not update otherwise. That might be your problem now (if you are manually reloading the page each time): your variable is only being passed via the redirect which again, you are not following and rendering.
Finally, I would say there are better ways to accomplish what you are trying to do. Maybe store the variable locally and update it in a database remotely. No need to reload the page for something this trivial.
Hope this helps. Please let me know if I have misunderstood your problem or I have made a mistake.
Related
This issue comes down to the fact that I haven't yet figured out how to pass data from javascript to Flask/Python via AJAX in the right way.
I have built a Flask application that uses JsPsych as a javascript/front-end framework for an online experiment. jsPsych allows you to create a handler function, on_finish(), that executes once the experiment is finished.
I wanted my experiment's data to be stored on the backend, and that was supposed to be the end o the experiment. I created a function called data_to_csv() that was mounted to the url /temp_storage, that did this. Then I created another url endpoint for /experiment_end, which displays a simple html page stating that the experiment is over and data was saved.
jsPsych.init({
...
on_finish: function() {
var subj_id = jsPsych.data.get().select('user_id').values[0];
var test_trials_collection = jsPsych.data.get();
var test_trial_json = test_trials_collection.json();
$.ajax({
type: "POST",
url: "/temp_storage",
data: {experiment_data:test_trial_json, webapp_id:subj_id},
success: function(response) {
window.location.href="/experiment_end";
},
error: function(xhr, textStatus, thrownError) {
alert('An error occurred while trying to save data');
console.log(thrownError);
}
});
},
});
The Flask side/the code for /temp_storage looked like...
#app.route('/temp_storage', methods=['GET', 'POST'])
def data_csv_func():
test_trial_json_obj = request.form['experiment_data']
subj_id = request.form['webapp_id']
test_trial_json_obj = json.loads(test_trial_json_obj)
df_data = pd.DataFrame(test_trial_json_obj)
csv_name = "app_dir/static/textdata/" + "appid_" + str(subj_id) + ".csv"
df_data.to_csv(csv_name)
return "Experiment is over!"
#app.route('/experiment_end', methods=['GET', 'POST'])
def show_qualtrics_after_experiment():
return render_template("end.html")
But now I don't want the AJAX call to simply end my experiment. I want my AJAX call to lead to a new webpage within the Flask application that contains a survey, take_qualtrics.html. I also need take_qualtrics.html to have, at the very least, the webapp_id string passed into it (but it would be ideal if I could also pass in the json object for jspsych's data, i.e. test_trials_json_obj). This is in order to link survey responses and web app experiment data
I initially just tried replacing the return statement in data_csv_func() to return render_template("take_qualtrics.html", data={"webapp_id":subj_id, "experiment_data":test_trial_json_obj}), and I deleted/commented out the windows.href("/experiment_end") call. I kept the AJAX call as is (the url was still /temp_storage), I found that this would execute data_to_csv() but the render_template() call wouldn't actually redirect my web page to another url.
So it seems like an AJAX call to a Flask function can't actually trigger redirects on the user's side. And I'm not sure how to work around this currently. I want to have some way of loading take_qualtrics.html and passing it the webapp id (created in javascript) as an argument.
I have a site that makes an AJAX post to a Flask route.
I know there are many similar questions (please don't mark as duplicate) that use the AJAX success method to handle the response. That would work in my simplified example, but the route in my actual code is pulling a bunch of data from a database that gets rendered to multiple tables. I'd prefer not to rewrite all the table data updates in JavaScript, so I really just need to re-render the template.
python:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def hello_world(message=None):
return render_template('test.html', message=message)
app.run()
html
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#mybutton').click(function () {
$.post(window.location.href, {'test': 'test'});
});
});
</script>
</head>
<body>
{{ message }}
<button id="mybutton" name="mybutton" type="button">Click Me!</button>
</body>
</html>
I've looked for the answer to this question on many pages and none of them worked until I found the one that releases the AJAX call so that the rerender is allowed to take place:
$('form').on('submit', function(event) {
$.ajax({
type: 'POST',
url: "{{ url_for( 'myServerFunction' ) }}",
data: JSON.stringify({'myId':bunchOfIds}),
contentType: "application/json",
success:function(response){ document.write(response); }
})
The most important line is the success line that releases the document so that it can be rerendered.
When you send POST request through AJAX, the request comes from javascript and response data from flask will be stored in a Javascript object. The browser itself doesn't directly send or receive anything, so it won't re-render the page.
You need to define another endpoint in your Flask app to return json data for your POST request, and use AJAX to manipulate the DOM using the returned json data.
Ok, why don't you just send a 'yes or no' flag from your flask app. Since you want to show the success or failure message without page refresh, getting jquery to update the DOM when your flask app returns a result. Something like
return message in your flask app might do the trick accompained by
success: function(e)({ $('body').append(e);});. Can't produce more code due to word length restriction on my phone, but tweaking your code along mine gets the job done. Cheers!
I apologize if this is unclear or anything like that. I'm very very new to web programming of any kind, so please be patient. When a link is clicked, I want to run a python script and then display the results. What is currently happening is it's just returning the HTML page. I have an idea of why but no clue how to fix it. I believe the issue is with the Flask python code, but please any input is appreciated. I will comment the area I believe to be the problem
Flask (Python) code:
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
return "Hello, world!"
#app.route('/cgi-bin/cputemp.py', methods=['GET', 'POST'])
#this is where need to put something, but I don't know what.
#Without defining this route I was getting a 405 error. I have no idea
#what would go here -- this is just the directory to the python and I
#thought the routes were for different web pages the user could access.
#Again, I believe *this* is the source of the problem. Obviously
#right now it's just returning the HTML of the following test() function.
#app.route('/test', methods=['GET', 'POST'])
def test():
return render_template("test.html")
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000, debug=True)
test.html
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="/static/test.js"></script>
<div id="swiss">Click to display CPU Temp</div>
</html>
test.js
function cputemp2()
{
$.ajax(
{
type: "POST",
url: "cgi-bin/cputemp.py",
dataType: "html",
success: function(msg)
{
console.log(msg); # It's just returning the HTML of test.html currently
document.getElementById('swiss').innerHTML = msg;
},
});
}
cputemp.py
#!/usr/bin/python
import cgi;
import cgitb;
import time
cgitb.enable()
import commands
import sys
import string
print "Content-type: text/html\n\n";
mytemp1 = commands.getoutput('/opt/vc/bin/vcgencmd measure_temp | cut -d "=" -f2 | cut -f1')
output = "Pi CPU Temp is: " + mytemp1
print output
My question is -- I thought the AJAX code in the test.js file would be handling the call to the python script. All it does is execute the method below the route to that directory in my Flask code. So do I need to run the python code there? How do I do this?
Thank you so much, I'm really lost and stuck on this.
There are a few things here that need to be fixed to get things working (or at least how I understand you want them to work).
If you're going to be using Flask, you don't need the route to point to a Python script. You can route to something like /cputemp and then run a function that returns the piece of HTML with the CPU temp that I presume you wanted to display.
#app.route('/cputemp', methods=['GET', 'POST'])
def cputemp():
mytemp1 = commands.getoutput('/opt/vc/bin/vcgencmd measure_temp | cut -d "=" -f2 | cut -f1')
return render_template("cputemp.html", temp=mytemp1)
Don't forget to import commands at the top. Although, you really should be using subprocess instead. https://docs.python.org/2/library/commands.html
The return there uses a Flask template to create the HTML fragment that you want to insert when the AJAX request is successful. http://flask.pocoo.org/docs/0.11/quickstart/#rendering-templates
For example, cputemp.html can simply be something like:
<p>Pi CPU Temp is: {{ temp }}</p>
Note that I don't know whether that command being assigned to mytemp1 works. That's a separate issue from not being able to display the information you want.
Now for the AJAX part. I added an error handler to help debug further issues. Note that I changed the URL to match the route. Also, using innerHTML has security issues, and rather than concerning yourself with sanitizing what you set innerHTML to, use jQuery's html function. http://api.jquery.com/html/
function cputemp2() {
$.ajax({
type: "POST",
url: "/cputemp",
dataType: "html",
success: function(msg) {
console.log(msg);
$("#swiss").html(msg);
},
error: function (xhr, status, error) {
console.log(error);
}
});
}
Hope this is enough to get you moving along.
This may sound simple, but how do I send the data from a Javascript array in my index.html template to my views.py?
When the user clicks a "Recommend" button, my code calls a function that accesses my database and prints a name on the template.
def index(request):
if(request.GET.get('Recommend')):
sql_handler.recFunc()
context['name'] = sql_handler.name
return render(request, 'polls/index.html', context)
I have an array of checkbox values in Javascript that are calculated after the user presses "Recommend". I want to send it to my index view and use it as the parameter for another function.
So:
def index(request):
if(request.GET.get('Recommend')):
sql_handler.recommend()
context['name'] = sql_handler.name
//something??
tags = check_array_javascript
context['tags'] = tags
return render(request, 'polls/index.html', context)
How can I do this? I've been searching similar questions, but I'm new to Django and web development in general, so I either did not understand the answers or they didn't help me.
Alright, so for sending data from the client (JavaScript) to the backend (your Django app) you need to employ something called Ajax, it stands for Asynchronous JavaScript and XML.
Basically what it does is allowing you to communicate with your backend services without the need of having to reload the page, which, you would have to do using a normal POST or PUT form submission.
The easiest implementation is using jQuery. jQuery is first and foremost a DOM manipulation library but since its inception has grown to encompass much more than that.
A jQuery ajax call looks like this.
$(document).ready(function() {
$.ajax({
method: 'POST',
url: '/path/to/your/view/',
data: {'yourJavaScriptArrayKey': yourJavaScriptArray},
success: function (data) {
//this gets called when server returns an OK response
alert("it worked!");
},
error: function (data) {
alert("it didnt work");
}
});
});
This can then be checked for in your views.py
def index(request):
if request.is_ajax():
#do something
request_data = request.POST
return HttpResponse("OK")
I'm a newbie to both Ajax and Django. To simplify the question assume I have one hidden form and two objects, and I ask the users to click some of the position of the objects. Each clicks fills out a part of the form. I want the form to be filled out once for each object. so twice overall. When the form is filled for the first subject I want to send the form via Ajax to the server, without refreshing the page, and allow the form to be refilled for the second object. However, for some reason I can't get the form to be send via ajax.
here is my code:
index.html:
<html>
<body>
<script src="{{ STATIC_URL }}prototype.js"></script>
<script src="{{ STATIC_URL }}jquery.js"></script>
<script>
objectID= 1;
num_obj = {{num_obj}}
function ajax_post(){
$('#inputform').submit(function() {
$.ajax({
type: 'POST',
url: '/index/',
data: $(this).serialize(),
success: function(data){alert(data)}
}); // end new Ajax.Request
});
}
function onDocumentMouseDown( event ) {
....do stuff
if (objectID < num_obj){
ajax_post()
}
if (objectID == num_obj){
$("form").submit();}
objectID ++;
$("form").clearForm();
document.forms[0].ObjectID.value = objectID;
}
</script>
<form action="" method="post" id="inputform">
<div id="ajaxwrapper">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
</div>
</form>
</body>
</html>
my view.py:
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import *
from firstapp.forms import PracForm3
num_obj=2
def index(request):
Obj.objects.all().delete()
if request.method == 'POST':
form = PracForm3(request.POST)
print "\n\n\n\n\n"
if request.is_ajax():
print "yaaaaay AJAX"
print request.POST
else:
print "noooooo"
print "\n\n\n\n\n"
if form.is_valid():
cd = form.cleaned_data
.....
if cd['ObjectID'] == num_obj:
return HttpResponseRedirect('/submit')
else:
form = PracForm3()
dic = {'form': form, 'num_obj': num_obj, ...}
return render_to_response('index.html', dic, context_instance=RequestContext(request))
my urls.py:
from django.conf.urls import patterns, include, url
from firstapp.views import index, submit
from django.conf import settings
urlpatterns = patterns('',
('^index$', index),
('^submit$', submit),
)
for some reason, my Ajax does not work anyone knows what I'm doing wrong?
This is probably related CSRF for AJAX posts, you have to remember to pass the CSRF token in as POST data with every POST request. You may need to add this code here, which are the Django docs that talk about Django+CSRF+AJAX.
I notice in your Django view that you are redirecting to "submit" Typically with ajax I would not redirect to a different url, I would use a HttpResponse and return some string value, such as JSON. I also don't know why you have print statements in your view, unless you are steeping into that view in a debugger. For testing/debugging you should have a variable that you assign some string to, and then pass that back to your ajax. Then in your ajax you can read the message from the view, something like this.
def index(request):
...
if request.is_ajax():
msg = 'it worked'
else:
msg = 'i am sad'
return HttpResponse(msg)
...
Also you javascript should have a success and error function in the ajax call, you only
have success so you are not catching errors, given the view tad bit your javascript would look like this:
...
success: function(data){alert(data)},
error: function(data){alert(data)}
...
It looks like you may have several issues going on. If you have not done the CSRF stuff that I gave you the link to that is an issue too, for testing use the csrf_exempt if you don't want to address doing it the preferred way. Try the code bits above and see if that makes progress for you.
The csrf token is included in $('#input-form') post request, so I don't think that was the issue. I think the problem is that you aren't converting the context into json in your view. This is from the docs example:
import json
from django.http import HttpResponse
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
"""
response_class = HttpResponse
def render_to_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
response_kwargs['content_type'] = 'application/json'
return self.response_class(
self.convert_context_to_json(context),
**response_kwargs
)
def convert_context_to_json(self, context):
"Convert the context dictionary into a JSON object"
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return json.dumps(context)
You're requesting /index/ in your ajax call, but your pattern matches /index.
It may caused by the csrf, add #csrf_exempt before the method name will be helpful.
#csrf_exempt
def method(args):
pass
I commented out the CSRF so it wasn't that my ajax function was the problem. Here is the correct way:
function ajax_post(){
$.ajax({
type: 'POST',
url: '/index/',
data: $(this).serialize(),
success: function(data){alert(data)}
}); // end new Ajax.Request
}
thanks for all your input