#app.route not working without redirection - javascript

This function is working perfectly if I do a redirect to another page #app.route('/results') but I'm struggling to make it work in the same page without reloading :
#app.route('/')
def static_file():
return app.send_static_file('index.html')
#app.route('/')
def results():
headers = {
'X-ApiKey': 'xxxxxxxxxxxxxxxx',
}
data = {'data': request.args.get('data')}
results = requests.post('https://apiv2.indico.io/personality', headers=headers, data=data)
return results.text
I'd like to display the results of the api call in the same page (below the input box)
<form action="" onsubmit="return false";>
<strong> Try It <br><br>
</strong><br>
<input type="text" name="data"><br><br>
<input type="submit" onclick="showResults();">
<label id:Results>Your Result: </label>
<p><span id='display'></span></p>
</form>
I've also added some JS:
<script language="JavaScript">
function showResults() {
document.getElementById('display').innerHTML =
document.getElementById("Results").value;
}
</script>
What's wrong with my code? I'm using Flask framework.

If I understood you correctly, what you want to achieve is to have RESTful API written in Flask microframework, which would allow you to obtain some results and display on your page without reloading. If so, your approach is wrong.
Firstly, you can't route multiple functions with common URL:
#app.route('/common_route')
def foo():
pass
#app.route('/common_route')
def bar():
pass
It is impossible to determine which function did you want to be invoked if you load http://yourwebsite/common_route.
In order to avoid page reloading, you should rather use Flask-RESTful module and implement an API for obtaining results.
import flask-restful
class Results(Resource):
def __init__(self):
super(Results)
#for GET requests
def get(self):
#your code here
pass
#for POST requests
def post(self):
#your code here
pass
api = Api(app)
api.add_resource(Results(),'/api/results')
Then you can obtain your results f.e. with JS & jQuery code: https://www.w3schools.com/jquery/jquery_ajax_get_post.asp

Related

Flask returning a generator and handling it in JavaScript [duplicate]

I have a view that generates data and streams it in real time. I can't figure out how to send this data to a variable that I can use in my HTML template. My current solution just outputs the data to a blank page as it arrives, which works, but I want to include it in a larger page with formatting. How do I update, format, and display the data as it is streamed to the page?
import flask
import time, math
app = flask.Flask(__name__)
#app.route('/')
def index():
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return flask.Response(inner(), mimetype='text/html')
app.run(debug=True)
You can stream data in a response, but you can't dynamically update a template the way you describe. The template is rendered once on the server side, then sent to the client.
One solution is to use JavaScript to read the streamed response and output the data on the client side. Use XMLHttpRequest to make a request to the endpoint that will stream the data. Then periodically read from the stream until it's done.
This introduces complexity, but allows updating the page directly and gives complete control over what the output looks like. The following example demonstrates that by displaying both the current value and the log of all values.
This example assumes a very simple message format: a single line of data, followed by a newline. This can be as complex as needed, as long as there's a way to identify each message. For example, each loop could return a JSON object which the client decodes.
from math import sqrt
from time import sleep
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/stream")
def stream():
def generate():
for i in range(500):
yield "{}\n".format(sqrt(i))
sleep(1)
return app.response_class(generate(), mimetype="text/plain")
<p>This is the latest output: <span id="latest"></span></p>
<p>This is all the output:</p>
<ul id="output"></ul>
<script>
var latest = document.getElementById('latest');
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('stream') }}');
xhr.send();
var position = 0;
function handleNewData() {
// the response text include the entire response so far
// split the messages, then take the messages that haven't been handled yet
// position tracks how many messages have been handled
// messages end with a newline, so split will always show one extra empty message at the end
var messages = xhr.responseText.split('\n');
messages.slice(position, -1).forEach(function(value) {
latest.textContent = value; // update the latest value in place
// build and append a new item to a list to log all output
var item = document.createElement('li');
item.textContent = value;
output.appendChild(item);
});
position = messages.length - 1;
}
var timer;
timer = setInterval(function() {
// check the response for new data
handleNewData();
// stop checking once the response has ended
if (xhr.readyState == XMLHttpRequest.DONE) {
clearInterval(timer);
latest.textContent = 'Done';
}
}, 1000);
</script>
An <iframe> can be used to display streamed HTML output, but it has some downsides. The frame is a separate document, which increases resource usage. Since it's only displaying the streamed data, it might not be easy to style it like the rest of the page. It can only append data, so long output will render below the visible scroll area. It can't modify other parts of the page in response to each event.
index.html renders the page with a frame pointed at the stream endpoint. The frame has fairly small default dimensions, so you may want to to style it further. Use render_template_string, which knows to escape variables, to render the HTML for each item (or use render_template with a more complex template file). An initial line can be yielded to load CSS in the frame first.
from flask import render_template_string, stream_with_context
#app.route("/stream")
def stream():
#stream_with_context
def generate():
yield render_template_string('<link rel=stylesheet href="{{ url_for("static", filename="stream.css") }}">')
for i in range(500):
yield render_template_string("<p>{{ i }}: {{ s }}</p>\n", i=i, s=sqrt(i))
sleep(1)
return app.response_class(generate())
<p>This is all the output:</p>
<iframe src="{{ url_for("stream") }}"></iframe>
5 years late, but this actually can be done the way you were initially trying to do it, javascript is totally unnecessary (Edit: the author of the accepted answer added the iframe section after I wrote this). You just have to include embed the output as an <iframe>:
from flask import Flask, render_template, Response
import time, math
app = Flask(__name__)
#app.route('/content')
def content():
"""
Render the content a url different from index
"""
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return Response(inner(), mimetype='text/html')
#app.route('/')
def index():
"""
Render a template at the index. The content will be embedded in this template
"""
return render_template('index.html.jinja')
app.run(debug=True)
Then the 'index.html.jinja' file will include an <iframe> with the content url as the src, which would something like:
<!doctype html>
<head>
<title>Title</title>
</head>
<body>
<div>
<iframe frameborder="0"
onresize="noresize"
style='background: transparent; width: 100%; height:100%;'
src="{{ url_for('content')}}">
</iframe>
</div>
</body>
When rendering user-provided data render_template_string() should be used to render the content to avoid injection attacks. However, I left this out of the example because it adds additional complexity, is outside the scope of the question, isn't relevant to the OP since he isn't streaming user-provided data, and won't be relevant for the vast majority of people seeing this post since streaming user-provided data is a far edge case that few if any people will ever have to do.
Originally I had a similar problem to the one posted here where a model is being trained and the update should be stationary and formatted in Html. The following answer is for future reference or people trying to solve the same problem and need inspiration.
A good solution to achieve this is to use an EventSource in Javascript, as described here. This listener can be started using a context variable, such as from a form or other source. The listener is stopped by sending a stop command. A sleep command is used for visualization without doing any real work in this example. Lastly, Html formatting can be achieved using Javascript DOM-Manipulation.
Flask Application
import flask
import time
app = flask.Flask(__name__)
#app.route('/learn')
def learn():
def update():
yield 'data: Prepare for learning\n\n'
# Preapre model
time.sleep(1.0)
for i in range(1, 101):
# Perform update
time.sleep(0.1)
yield f'data: {i}%\n\n'
yield 'data: close\n\n'
return flask.Response(update(), mimetype='text/event-stream')
#app.route('/', methods=['GET', 'POST'])
def index():
train_model = False
if flask.request.method == 'POST':
if 'train_model' in list(flask.request.form):
train_model = True
return flask.render_template('index.html', train_model=train_model)
app.run(threaded=True)
HTML Template
<form action="/" method="post">
<input name="train_model" type="submit" value="Train Model" />
</form>
<p id="learn_output"></p>
{% if train_model %}
<script>
var target_output = document.getElementById("learn_output");
var learn_update = new EventSource("/learn");
learn_update.onmessage = function (e) {
if (e.data == "close") {
learn_update.close();
} else {
target_output.innerHTML = "Status: " + e.data;
}
};
</script>
{% endif %}

How would I transfer information like a variable from javascript to python using AJAX and Flask

I am messing around with Flask and Ajax to see what I can do with it for a bigger project I'm working on. I am trying to get some data in a variable into my python program to be able to use and manipulate. I have a script in an html file that returns the current URL. How can I get the url into my python program with AJAX?
I will attach my relevant code below:
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HELP</title>
</head>
<body>
<p>I am {{person}} and i am {{age}} years old, I am a {{ql}}</p>
<form name="passdata" action="." method="post">
<label>Name:</label>
<input type="text" name="name">
<label>Age:</label>
<input type="text" name="age">
<label>Date of Birth:</label>
<input type="text" name="dateofbirth">
<input type="submit" value="submit">
</form>
<p id="demo"></p>
<script>
let geturl = window.
document.getElementById("demo").innerHTML =
"The full URL of this page is:<br>" + geturl;
</script> //getting the current url
</body>
</html>
main.py
from flask import Flask, render_template, request
app = Flask(__name__)
name = "random"
age = "21"
qualification = "software engineer"
#app.route('/')
def index():
return render_template('index.html', person=name, age=age, ql=qualification)
#app.route('/', methods=['POST'])
def getvalue():
name2 = request.form['name']
age = request.form['age']
db = request.form['dateofbirth']
return name2
if __name__ == '__main__':
app.run(debug=True)
let me know if you need any clarification. Thanks in advance.
Usually when you are sending variables to routes in flask you will have the route designed as:
#app.route('/route_to_send_to/<variable>', methods=['GET'])
def route_to_send_to(variable):
variable_sent = variable
return jsonify({'variable':variable})
Another hint usually when working with ajax calls the response may be a json response which you can send back with jsonify.
All you need to do is call it to the route replacing with the variable you want to send.
You can also send with Ajax a post request to have the data hidden, but then you will need to change the route to handle post requests.
The Ajax call may be something like:
var url = '/route_to_send_to/ + name;
return $.ajax({
type: "GET",
url: url,
}).then(function(data) {// Do something with the responce})
});
Considering the comments below I might suggest you read this article to get a better understanding of how flask and ajax work together.
Also This amazing Flask tutorial by Miguel Grinberg is probably one of the best resources I have ever come across for learning flask, the framework and good conducts with it. It goes through everything from the absolute basic to extremely advanced topics and is well worth the time.

How to connect JavaScript to Python script with Flask?

I created a website with HTML/CSS. I also used Javascript for events (click on button, ...).
Now I want to connect a Python script with it and more importantly, return the results from my Python functions to my website and display (use) them there.
Consider something like this: I have a website with an input field and a button. If you click on the button, a Python script should run which returns if the input is an odd or even number (of course you don't need Python for this specific case, but that's what I want to do).
From my research I believe Flask is the library to be used for this, but I really don't know how to do it. I found very few examples. I would really appreciate if someone could implement the above example or tell me how to do it exactly.
I know there are already some questions about that concept here online, but as I said, with very few examples.
You're right about Flask being a good solution for this and there are examples and tutorials everywhere. If what you want is just to run a specific function on a button press and get something back in javascript, I've put a quick example is below.
# app.py
from flask import Flask, render_template
from flask import jsonify
app = Flask(__name__)
# Display your index page
#app.route("/")
def index():
return render_template('index.html')
# A function to add two numbers
#app.route("/add")
def add():
a = request.args.get('a')
b = request.args.get('b')
return jsonify({"result": a+b})
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
This can then be run with python app.py and make sure your index.html is in the same directory. Then you should be able to go to http://127.0.0.1/ and see your page load.
This implements a function which adds two numbers, this can be called in your javascript by calling http://127.0.0.1/add?a=10&b=20. This should then return {"result": 30}.
You can grab this in your javascript using the code below and place this code in your buttons on click callback.
let first = 10;
let second = 20;
fetch('http://127.0.0.1/add?a='+first+'&b='+second)
.then((response) => {
return response.json();
})
.then((myJson) => {
console.log("When I add "+first+" and "+second+" I get: " + myJson.result);
});
This should be the barebone basics, but once you can submit data to Flask and get data back, you now have an interface to run things in Python.
Edit: Full Front-end example
https://jsfiddle.net/4bv805L6/
I really appreciate time spent on this answer. But the answer did not help me in the way I needed it. At that point I had no clue what to do, but since thenbI figured it out some time ago and I thought I should share my solution here:
That's app.py:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/stick', methods=['GET', 'POST'])
def stick():
if request.method == 'POST':
result = request.form['string1'] + request.form['string2']
return render_template('index.html', result=result)
else:
return render_template('index.html')
if __name__ == "__main__":
app.run()
And that's index.html (put in the folder templates):
<!DOCTYPE html>
<html>
<body>
<h3> Stick two strings </h3>
<form action="{{ url_for('stick') }}" method="post">
<input type="text" name="string1">
<input type="text" name="string2">
<input type="submit" value="Go!">
<p id="result"></p>
</form>
<script>
document.getElementById("result").innerHTML = "{{result}}"
</script>
</body>
</html>
In the terminal, type in python app.py and it should work.

Having trouble handling AJAX with Django Class Based View

I've been trying to switch my codes from Django function based view to class based view, but I'm having trouble understanding how to handle AJAX in Django CBV.
For example, suppose that I have this live search feature in my Django blog project.
(sorry, I tried to make it as simple as possible.)
urls.py
url(r'^search/$', search_page),
forms.py
class SearchForm(forms.Form):
query = forms.CharField()
entire_search_page.html
<form id="search-form" method="get" action=".">
{{form.as_p}}
<input type="submit" value="search" />
</form>
<div id="search-results">
{% include "just_the_result.html" %}
</div>
just_the_result.html
{% for post in post_list %}
<li>{{ post.title }}</li>
views.py
def search_page(request):
form = SearchForm()
post_list = []
if 'query' in request.GET:
form = SearchForm({'query': query})
post_list = Post.objects.filter(title__icontains=query)
if request.GET.has_key('ajax'):
variables = RequestContext(request, {
'post_list': post_list,
})
return render_to_response('just_the_result.html', variables)
else:
variables = RequestContext(request, {
'form': form,
'post_list': post_list,
})
return render_to_response('entire_search_page.html', variables)
search.js
$(document).ready(function() {
$("#search-form").submit(search_submit);
});
function search_submit() {
var query = $("#id_query").val();
$("#search-results").load(
"/search/?ajax&query=" + encodeURIComponent(query)
);
return false;
}
So basically, I'm showing the entire search page when the request is normal, while only showing the "just_the_result.html" if the request is AJAX. This works fine when I run the server, but I want to change this view to Class Based View.
This is what I have so far:
views.py
class PostSearch(ListView):
model = Post
template_name = 'app_blog/search.html'
context_object_name = 'post_list'
def get_queryset(self):
queryset = super(PostSearch, self).get_queryset()
query = self.request.GET.get('query')
if query:
return queryset.filter(title__icontains=query)
else:
return queryset
I guess this works correctly when the request is normal. However, I have no idea what to do when the request is an AJAX request! In the function based view, I could just return different templates and variables depending on whether the request is AJAX or not, but I don't think this is necessary or even possible in CBV. Also, one more question. I've been reading about RESTful design lately. Is my code above considered as "RESTful"?? I have trouble understanding how REST API is used exactly.
There are a lot of solutions to this problem, just search around:
http://django-braces.readthedocs.org/en/latest/other.html#ajaxresponsemixin
https://docs.djangoproject.com/en/1.5/topics/class-based-views/mixins/#more-than-just-html
Using Django class-based views, how can I return a different template if request.is_ajax

How to pass data by 'POST' method to from Javascript to Python

I have this part of script from my GAE application which uses webapp2, which accepts data from a form using post,
class RenderMarksheet(webapp2.RequestHandler):
def post(self):
regno = self.request.get('content') # Here's where I extract the data from the form
...
...
...
self.response.out.write(template.render(templates/render.html, template_values))
And the web form which posts to this script,
<form action="/sign" method="post" name="inputform" onsubmit="return validate()">
Register No : <input type="number" name="regno" placeholder="Your Register No."/>
<input type="submit" value="Get-My-GPA!" >
</form>
Now, I want to manually pass a specific data (a register no.), without using the submit button from the form, to the python script( or the url, perhaps) , using Javascript, say a button that triggers a javascript method.
I have to POST the data using javascript(to implement AJAX).
In python I do this, to post the data to a url,
import http.client, urllib.parse
params = urllib.parse.urlencode({'regno':10109104021})
headers = {"Content-type": "application/x-www-form-urlencoded",
"Accept": "text/plain"}
conn = http.client.HTTPConnection("mydomain:8888")
conn.request("POST", "/sign", params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
How can I post the data to the url, via Jquery or Javascript?
fastest is to use jQuery and use $.post()
I'm not 100% sure what you're trying to do, but after several rereads here's what I think you're trying to do.
you'll need to import urllib2
#import google classes
import urllib2
class RenderMarksheet(webapp2.RequestHandler):
def parseResponse(self, response):
#run some code in here to parse the results since it's an HTML document ... beautifulsoup, perhaps?
def post(self):
regno = self.request.get('content')
rawlink = "http://result.annauniv.edu/cgi-bin/result/result11gr.pl?regno="
link = rawlink+regno
try:
result = urllib2.urlopen(link)
gpa = parseResponse(result)
except urllib2.URLError, e:
self.response.out.write(e)
template_values = {'gpa': gpa}
self.response.out.write(template.render(templates/render.html, template_values))
This method will
Take the input from the form
Build the link
Request information from the annauniv webserver
Parse the response from that server (you're on your own parsing that, but you'd have to do it regardless)
Store the GPA a templates dictionary for use in your template

Categories

Resources