How to call a python script through an AJAX call? - python

I have the following python script:
from flask import Flask, render_template, json, request
from flask import Flask, request, abort, jsonify
from flaskext.mysql import MySQL
import MySQLdb
app = Flask(__name__)
#app.route("/")
def main():
return render_template('testFirst.html')
#app.route("/form_submit/", methods=['POST'])
print ("outside function")
def connect():
import json
dtb = request.select['value'] #i want the selected drop down value to be set here which will be used to connect to the database
db = MySQLdb.connect("localhost","root","",dtb)
cursor = db.cursor()
cursor.execute("SELECT * FROM REPORT_SUITE")
results = cursor.fetchall()
json_return_value =[]
for result in results:
table_data = {'REPORTSUITE_ID' : result[0], 'REPORTSUITE_NAME' : result[1], 'STAGING_DATABASE' : result[2], 'DWH_DATABASE' : result[3], 'TRANS_TABLE' : result[4]}
json_return_value.append(table_data)
print ("hi")
print json.dumps(json_return_value)
return json.dumps(json_return_value)
connect()
if __name__ == "__main__":
app.run(debug = True)
It renders the following html file:
$(document).ready(function(){
$("button").click(function(){
$.ajax({
url : "/form_submit/",
data : $('#databases').val(),
type : 'POST',
success : alert("Hi dear count ")
});
});
});
<body>
<select id ="databases">
<option value = "">--Select From following--</option>
<option value = "OMTSL_QBO">OMTSL_QBO</option>
<option value = "OMTSL_SBG">OMTSL_SBG</option>
<option value = "OMTSL_PTX">OMTSL_PTX</option>
</select>
<br><br>
<button type = "button"> Click Me!!</button>
<div id ="placeholder"></div>
</body>
</html>
The HTML form should call the python script through an ajax call by passing the selected value from the drop down to the connect() method.
when i run main.py, i get the following error
Traceback (most recent call last):
File "main.py", line 31, in <module>
connect()
File "main.py", line 16, in connect
dtb = request.select['value']
File "/Library/Python/2.7/site-packages/werkzeug/local.py", line 338, in __getattr__
return getattr(self._get_current_object(), name)
File "/Library/Python/2.7/site-packages/werkzeug/local.py", line 297, in _get_current_object
return self.__local()
File "/Library/Python/2.7/site-packages/flask/globals.py", line 20, in _lookup_req_object
raise RuntimeError('working outside of request context')
RuntimeError: working outside of request context
I do not know how to pass the drop down value to the called python fucntion. I tried to look for it but i have still not found the way to
do it. Please help me through this

Ajax uses HTTP, so you cant call a python script directly (unless you have specifically built your setup to work that way).
Your ajax should look something like:
$(document).ready(function(){ $("button").click(function(){ $.ajax({ url : "/form_submit/", data : $('#databases').val(), type : 'POST', //success : alert("Hi "+ $('#databases').val()) success : alert("Hi dear count ") }); }); });
Then have a Flask function decorated with #app.router('/form_submit/') under which you would then handle the form data as per the Flask tutorial.

You shouldn't have a separate "Python script". It should be another handler in your Flask app, with a route, which your Ajax script calls.

In your second code, you're sending the call from ajax using POST, while in your Flask code you have
#app.route("/form_submit/", methods=['GET'])
Change this line into:
#app.route("/form_submit/", methods=['GET', 'POST'])

Related

500: Internal Server Error Spotify python Auth Code flow trying to redirect to login and get token

I am using this documentation to get the user to login and return me code and state. However, when I run my app It gives me a 500 error.
This is my app.py
import flask
import SpotifyOAuth
app = flask.Flask(__name__)
#app.route('/')
def index():
flask.render_template("index.html")
#app.route('/login')
def accessSpotify():
SpotifyOAuth.RedirectTologin(redirect_uri='https://localhost:7001/authorized')
#app.route('/authorized')
def SP_redirect_uri():
return "spotify connected"
def main():
print("nothin")
if __name__ == '__main__':
app.run(use_reloader=True,port=7001)
and this is my SpotifyOAuth.py
import requests
def RedirectTologin(redirect_uri="https://localhost:7001/authorized"):
token_uri="https://accounts.spotify.com/authorize"
method="GET"
params={
"client_id" : '<id>',
"response_type" : 'code',
"redirect_uri" : redirect_uri,
"scope" : 'user-read-email'
}
client_secret='<secret>'
r = requests.get(token_uri,params=params)
print(r)
if __name__=='__main__':
RedirectTologin()
I know my RedirectTLogin() is working because when I print r it gives me response code 200. Not sure where I am going wrong in app.py
Here is my index.html for reference
<html>
<body>
<div>
<div id="login">
<h1>First, log in to spotify</h1>
Log in
</div>
<div id="loggedin">
</div>
</div>
</body>
</html>
Does anyone see any issue? I don't have much experience working with APIs
Flask routes need to return something: a rendered template, a redirect, plain text with a status code, etc.
To fix your issue you need to add returns to index() and accessSpotify() as follows:
#app.route('/')
def index():
return flask.render_template("index.html")
#app.route('/login')
def accessSpotify():
SpotifyOAuth.RedirectTologin(redirect_uri='https://localhost:7001/authorized')
return flask.redirect(url_for('SP_redirect_uri'))

How to make Flask run function in HTML and print out the result on the same page?

I've been trying to use my script that I previously written to turn Arabic numerals to Roman. User inputs numbers and my script turns them to Roman ones. Script runs fine, but me trying to embed it to a webpage is not working as intended. I googled solutions to that and everyone tell I need to get the value from a form, which I did:
<form action="toroman" method="POST">
<input type="number" name="arabic" onChange="toroman(this.value)" oninput="toroman(this.value)">
<p>{{ romanfinal }}</p>
</form>
And this is my server.py that should be printing out the number of the same page, but it doesn't do that, instead when I press "Enter" when submitting the value it creates a new page and displays correct answer.
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/toroman", methods=['POST'])
def toroman():
arabic = request.form['arabic']
# some unnecessarily numerous lines of code that basically turn Arabic-system number to Roman system
return romanfinal
if __name__ == "__main__":
app.run(debug=True)
This is like the only time it actually worked, when I try to change it so something else, it just gives me errors. Please tell me what exactly I don't understand about it.
Your toroman(): function should return index.html with the parameter :
#app.route("/toroman", methods=['POST'])
def toroman():
arabic = request.form['arabic']
# some unnecessarily numerous lines of code that basically turn Arabic-system number to Roman system
return render_template("index.html", data = romanfinal)
Then you can use the value data in your HTML top level like that : {{data}}
FLASK
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/toroman", methods=['POST'])
def toroman():
arabic = request.data['arabic']
#pass arabic into your translation function
translation = translate()
#return JSON response to AJAX
return jsonify(translation = translation)
if __name__ == "__main__":
app.run(debug=True)
JS
$(document).ready(function(){
document.getElementById('toroman_form').addEventListener('keyup', function() {
$.ajax({
type: 'POST',
url: '/toroman', //flask route to which data is sent
data: $('#arabic').val(), // input field value
contentType:'application/json; charset=utf-8',
dataType: "json",
success: function(data) {
translation = data.translation //response received from flask
$('#translated').text(translation) //display translation in html <p> tag
},
error: function() {
alert("Transaction Failed");
}
});
});
}
HTML
<form id="toroman_form">
<input type="number" id="arabic">
<p id="translated"><!--translation is dislayed here--></p>
</form>

Accessing a url in a Flask view

I am trying to access an external url https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json in a Flask view
I get the error,
Not Found
The requested URL was not found on the server. If you entered the URL
manually please check your spelling and try again.
Is that my local server that flask is looking for this url on. And if so why? I am running flask locally.
The view, services.py
from flask import Flask, Response
import json
import urllib2
app = Flask(__name__)
#app.route('/')
def test():
return 'Everything is running!'
#app.route('/stopid')
def stopid():
dublin_bus_url = "https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json"
response = urllib2.urlopen(dublin_bus_url)
json_response = json.load(response)
routes = set()
for result in json_response["results"]:
routes.add(result["route"])
return json.dumps(list(routes))
if __name__ == '__main__':
app.run()
The index.html and script is,
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
d3.json("/stopid", function(error, routes) {
routes.forEach(function(route) {
console.log(route)
});
});
</script>
</body>
</html>
I am new to Flask but this must not be the way to deal with an external link in a view.
The code above is adopted from this excellent tutorial for the Donorschoose api.
https://youtu.be/bzl4hCH2CdY
https://github.com/VidyaSource/starting-with-data
Thanks,
If we assume that the HTML file is not being served by flask:
You need to enable Cross origin resource sharing. You can do this by creating a response and setting it's header Access-Control-Allow-Origin to *: that is everyone. Or you can set it to your own domain when deploying.
resp.headers['Access-Control-Allow-Origin'] = '*'
Also, you're calling d3.json("/stopid" ... you need to change this to:
d3.json("http://localhost:5000/stopid" ...
Complete code:
from flask import Flask, Response, jsonify
import json
import urllib2
app = Flask(__name__)
#app.route('/')
def test():
return 'Everything is running!'
#app.route('/stopid')
def stopid():
dublin_bus_url = "https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json"
my_response = urllib2.urlopen(dublin_bus_url)
json_response = json.load(my_response)
routes = set()
for result in json_response["results"]:
routes.add(result["route"])
resp = jsonify(list(routes))
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
if __name__ == '__main__':
app.run()
If the HTML is being served by flask, there is no need to enable cross origin sharing.
#app.route('/d3')
def d3():
return render_template('d3.html')
Call the link to this url using:
d3.json("{{ url_for('stopid') }}", ...
But this isn't exactly reliable, because you don't want to use the api using javascript when you can do it in flask itself.

Handling POST requests using Pythons web.py

I have a very simple server. I use Python 2.7 with web.py.
Basically, my code looks like this:
urls = ("/endpoint", "Endpoint")
class Endpoint(object):
def GET(self):
return "End point"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Endpoint POST with payload: " + json.dumps(result))
return "Endpoint POST"
I tested this server by making POST requests like this:
echo '{"field": "test"}' | curl -d #- http://my.ip.number:port/endpoint
I tried server other methods of making POST requests. I also tried making get requests, both from the terminal and the browser.
In all cases, I get this very strange error.
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 239, in process
return self.handle()
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 229, in handle
fn, args = self._match(self.mapping, web.ctx.path)
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 427, in _match
for pat, what in mapping:
ValueError: need more than 1 value to unpack
Why is this error occurring and what can I do to prevent it?
Thanks!
EDIT 1:
After the traceback is displayed, I also get this:
192.168.46.1:51390 - - [16/Mar/2016 12:54:08] "HTTP/1.1 GET /favicon.ico" - 500 Internal Server Error
Neither the IP nor the port are the ones that I am using.
EDIT 2
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Unicode
from __future__ import unicode_literals
import sys
reload(sys)
# -----
# Logging
import logging
logging.basicConfig(level=logging.INFO)
# -----
# Libs
import web
import json
# -----
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
class Index(object):
# In the browser, this displays "Index", but also causes the error on the server side.
def GET(self):
return "Index"
# Doesn't do anything, but causes the error
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Index " + json.dumps(result))
return "Index POST"
class EP1(object):
def GET(self):
return "EP1"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP1 " + json.dumps(result))
return "EP1 POST"
class EP2(object):
def GET(self):
return "EP2"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP2 " + json.dumps(result))
return "EP2 POST"
if __name__ == "__main__":
logging.info("[Server] Starting server.")
app = web.application(urls, globals())
app.run()
This is how my server looks like.
I start the server like this:
python server.py 0.0.0.0:7331
If I access the server's root endpoint from the browser, I get "Index" and the error still occurs. The other two endpoints don't return anything and cause the error.
You're missing a comma at second line here:
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
It should be like this:
urls = ("/", "Index",
"/endpoint1", "EP1",
"/endpoint2", "EP2")
What happens without the comma is that Python concatenates the two strings without a comma in between.
So with your code, urls was actually
("/", "Index", "/endpoint1", "EP1/endpoint2", "EP2")

How to use Flask context in Cloud Endpoints

I built a web app with a REST API using Flask. I take advantage of Flask's g to save the current user and pull the user's data I want from the datastore (the app is hosted at Google Cloud). However, I would like to implement Google Cloud Endpoints because of some of its advantages but if I call one of the urls in Cloud Endpoints I get the error:
Traceback (most recent call last):
File "/Users/manuelgodoy/Documents/Google/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 239, in Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
File "/Users/manuelgodoy/Documents/Google/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 298, in _LoadHandler
handler, path, err = LoadObject(self._handler)
File "/Users/manuelgodoy/Documents/Google/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 95, in LoadObject
__import__(cumulative_path)
File "/Users/manuelgodoy/Projects/Eatsy/Eatsy/src/application/apis.py", line 18, in <module>
user = g.user
File "/Users/manuelgodoy/Projects/Eatsy/Eatsy/src/lib/werkzeug/local.py", line 338, in __getattr__
return getattr(self._get_current_object(), name)
File "/Users/manuelgodoy/Projects/Eatsy/Eatsy/src/lib/werkzeug/local.py", line 297, in _get_current_object
return self.__local()
File "/Users/manuelgodoy/Projects/Eatsy/Eatsy/src/lib/flask/globals.py", line 27, in _lookup_app_object
raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
How can I use flask's context variables like g, login_required, current_user, etc. for Cloud Endpoints?
In my code I store current_user in g.user and I have an endpoint where I get the g.user so I can get the id.
views.py:
from flask.ext.login import login_user, logout_user, current_user, login_required
from flask import session, g, request
import requests
#app.before_request
def before_request():
log.info('Received request: %s' % request.path)
g.user = current_user
#app.route('/recommendations', methods = ['GET'])
def recommendations_retrieve():
# This HTTP call is what I'd like to get rid off
app_url = request.url_root
usr_id = g.user.key().id()
d = {'id': str(usr_id)}
r = requests.get(urljoin(app_url,"/_ah/api/myapp/v1/recommendations"),
params = d)
return (r.text, r.status_code, r.headers.items())
My Cloud Endpoints file looks like this:
from views import g
#endpoints.api(name='myapp', version='v1', description='myapp API',
allowed_client_ids=[WEB_CLIENT_ID, endpoints.API_EXPLORER_CLIENT_ID])
class MyAppApi(remote.Service):
#endpoints.method(IdRequestMessage, RecommendationsResponseMessage,
path='recommendations', http_method='GET',
name='recommendations.recommendations')
def recommendations(self, request):
# I would prefer to use this, but I get the
# "Working outside the app context" error
# when I uncomment it
#user = User.get_by_id(g.user.key().id())
user = User.get_from_message(request)
response = user.get_recommendations()
return response
My Javascript function is as follows:
loadRecommendationsFromServer: function() {
$.ajax({
// This is how I *would* call it, if it worked
//url: this.props.url+"/_ah/api/myapp/v1/recommendations",
//data: JSON.stringify({'id':2}),
url: this.props.url+"/recommendations",
dataType: 'json',
success: function(data) {
this.setState({data: data.recommendations});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
The existing code works - how can I avoid having to make an HTTP request in my view handler and avoid the RuntimeError in the MyAppApi service when I use g.user?

Categories