I have an existing python API using flask that submits a post request in the body, then calls and executes another python script:
testingREST.py:
import testingdf
import json
from flask import Flask, json, request
app = Flask(__name__)
#app.route("/results", methods=['POST'])
def createResults():
entry = request.get_json().get('entry', '')
passcode = request.get_json().get('passcode', '')
data = testingdf.test(entry, passcode)
response = app.response_class(
response=json.dumps(data),
status=200,
mimetype='application/json'
)
return response
if __name__ == "__main__":
app.run(debug = True, host='localhost', port=8080, passthrough_errors=False)
testingdf.py:
import pandas as pd
def test(entry, passcode):
df = pd.DataFrame()
testInput = zip(entry, passcode)
for entry, passcode in testInput:
df = df.append({'Entry': entry, 'Passcode': passcode}, ignore_index=True)
results = {
'Entry' : entry,
'Outcome' : passcode,
}
return results
This is what I post into the request body when calling the api
{
"entry":[21, 44, 31],
"passcode":["denied", "Try Again", "Retry"]
}
Response:
{
"Entry": 35,
"Outcome": "Success"
}
Problem: In the request body, instead of having one variable for 'entry' and another for 'passcode', is there a way that I have something like this as the request body:
{
"entryInfo":[(21, "denied"), (44, "Try Again"), (31, "Retry")]
}
Basically, passing them in as pairs since I think the way I've implemented it is weird where it can easily be mismatched. Also, I'm not sure if "key/value" is the correct term to use here but I hope this helps.
Using python3 by the way.
Your question seems to boil down to what data to pass to the flask server. There is also another issue about what you do with that data (you're only returning the last one), but you did not ask about that.
Send this as the request body. It uses list notation which is valid JSON. Your flask server can then access the list by decoding the JSON request.
{"entryInfo": [[21, "denied"], [44, "Try Again"], [31, "Retry"]]}
A better way though might be to use a dictionary mapping entry codes to pass codes:
{"entryInfo": {"21": "denied", "44": "Try Again", "31": "Retry"}}
and it's debatable whether the top-level dict is required, you could get away with:
{"21": "denied", "44": "Try Again", "31": "Retry"}
Related
I am making ajax call to flask function to get data using a token as following
#app.route("/questgen/results", methods = ['POST', 'GET'])
def fetch_results():
token = request.form.get('token')
print(token)
conn = sqlite3.connect("database.db" , timeout=20)
cur= conn.cursor()
select_query = '''SELECT processed, output_text
FROM results
WHERE token = token
'''
cur.execute(select_query)
records = cur.fetchall()
processed = ""
html = ""
for row in records:
processed = row[0]
html = row[1]
conn.close()
data = {'processed': processed, 'html' : html}
return redirect(url_for('questgen', data = data))
ajax call is as following
$.ajax({
type: "POST",
url: "/questgen/results",
data: { token: token },
datatype: "json",
success: function (data) {
if (data.processed == 1) {
$('#divresults').html(data.html);
$('#divresults').show();
hideMyModal();
clearInterval(saveInterval);
}
}
});
the complete is a bit lengthy it can be found in this gist the problem is that it returns
TypeError: The view function did not return a valid response. The
function either returned None or ended without a return statement.
even though I have tried same flask function as python function by using return data on the same database and it works. I have even tried to get token as function parameter but still it's not working. Can someone help with what am I doing wrong here? Thank you
The jQuery ajax call does not handle redirects automatically. You'll just get a response with a 301 or 302 status code. If you really need to have it redirect, you'll need to check for a 302 status return and make the call again with the changed data. It would be better if you could just do the redirection internally by calling the other function.
Try jsonify to return data
from flask import Flask, jsonify, request #import this
And then use this to return data
return jsonify({"res": data})
In ajax you will get your data in res
console.log(data.res) // your data
console.log(data.res.processed) // your if condition
Also check whether you need to parse response body or not
So I'm using flask to create endpoints as receivers and data processors. I have two threads of http POST requests, the first one is sent to the first route, similarly for the second one. The thing is I want the 2nd processor to be triggered only when the 1st one is, so I created a session key, to validate for the execution of the 2nd processor.
But no matter what I did, session key is always wiped when I sent POST to the second processor. Here's my code, which has been simplified. Pardon my amateur ablity to express the problem, I'm extremely new to coding.
from flask import Flask, request, redirect, url_for, session
app = Flask(__name__)
app.secret_key = "abc"
#app.route('/first_processor', methods=['POST'])
def first_processor():
data = {
'message': 'json received',
'json': request.json
}
cond = data['json']
if cond['event'] == "message:received":
session["key"] = cond['key']
return redirect(url_for("second_processor"))
else:
return data
#app.route('/second_processor', methods=['POST'])
def second_processor():
if "key" in session:
print('OK')
else:
print("FAIL")
return data
if __name__ == "__main__":
app.run(debug=True)
Apparently I saw two minor problems. The first one is that
#app.route('/second_processor', methods=['POST']) `
only allows POST method, and
redirect(url_for("second_processor"))
is a GET request. And you cannot force a POST request. Even though, according to the documentation, there is a _method parameter in the url_for function.
Related question: Issue a POST request with url_for in Flask
The second problem is that you created the data variable inside the first_processor function, but you don't pass it to the second_processor.
if 'key' in session:
print('OK')
else:
print("FAIL")
--> return data
you could either:
pass the data inside the session,
make data global ( not sure if it is a good practice though)
store data in a file or db and read it inside second_processor.
The application/json in the request header and json string in the request body when I initiate an http request , the Odoo server receives the request, but the json returned to the client is not what I want to return.
Here are two additional key,jsonrpc,id,result.The dictionary corresponding to the key result is what I really want to return to the client.
And if I change the type variable in the http.route to http instead of json, I will can't receive json format data from the client.
What shoul I do?Thanks everyone!
My Odoo version is 10,python version is 2.7.12
Here is my code
controllers.py
from odoo.http import Controller,route
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'])
def access_something(self,**kwargs):
return {"a":1,"b":2}
Test interface with requests
import requests
re = requests.get('http://192.168.1.55:8069/v1/access_something',json={"c":1},headers={'Content-Type':'application/json'})
print(re.json())
The data in re.json()
{
"jsonrpc": "2.0",
"id": null,
"result": {
"a": 1,
"b": 2
}
}
But the following result is what I want.
{
"a": 1,
"b": 2
}
I've found a way to solve this problem.
This problem arises because there is a method _json_responsein the source code JsonRequestthat we can overwrite dynamically.
In order not to interfere with the use of the original framework by others, we can pass our own specific parameters in our own decorator#http.routeby using kwargs. We construct the json dictionary we need to return to the client by determining whether the decorator has our own parameters.
Here is my codecontrollers.py
from odoo.http import Controller,route,JsonRequest
def _json_response(self, result=None, error=None):
lover = self.endpoint.routing.get('lover')
if lover == 'chun':
response = {}
if error is not None:
response['error'] = error
if result is not None:
response = result
else:
response = {
'jsonrpc': '2.0',
'id': self.jsonrequest.get('id')
}
if error is not None:
response['error'] = error
if result is not None:
response['result'] = result
if self.jsonp:
# If we use jsonp, that's mean we are called from another host
# Some browser (IE and Safari) do no allow third party cookies
# We need then to manage http sessions manually.
response['session_id'] = self.session.sid
mime = 'application/javascript'
body = "%s(%s);" % (self.jsonp, json.dumps(response),)
else:
mime = 'application/json'
body = json.dumps(response)
return Response(
body, headers=[('Content-Type', mime),
('Content-Length', len(body))])
setattr(JsonRequest,'_json_response',_json_response) #overwrite the method
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'],lover='chun')
def access_something(self,**kwargs):
return {"a":1,"b":2}
The specific parameter lover='chun' is basis of our judgment.In method _json_response,we can get this parameter through self.endpoint.routing.get('lover')
I'm using DRF to make a GET request to another server and return this reponse to the client.
What I want to know is how do I get only selected fields from that other server's response to return to my client.
What I have is a response like this:
{
"plans": [
{
"setup_fee": 500,
"amount": 990,
"code": "plano01",
}
{
"setup_fee:...
"code": "plano02",
}...
An array with many objects. And I want to give to the client something like this:
{
"plans": [
{
"code": "plano01",
}
{"code": "plano02"...
Only the code field.
Whats the best way to do this with Django/DRF?
There's not much point in using DRF here. You're getting a response in JSON format, you just need to parse it to a dict, extract the elements you want, and return it back as JSON again; DRF would be overkill. The view can simply be:
def get_plan_codes(request):
data = requests.get('external_api...').json()
codes = [{'code': plan['code']} for plan in data['plans']]
data['plans'] = codes
return JsonResponse(data)
I don't think I would use DRF for this personally, although you could.
I think this could be done in a fairly straight foward way using just requests and django itself.
Something like:
from django.http import JsonResponse, HttpResponse
import json
import requests
def plans(request):
response = requests.get("my url for the request")
if response.status_code == 200:
json_content = json.loads(response.content)
# json_content is a python dictionary, so you can just postprocess the way you want it here
return JsonResponse(json_content)
else:
return #You will want to return an HttpResponse with the appropriate status code, see the docs below on that
# https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse.status_code
Given a simple Flask application, I'm just curious about whether there is a proper way to modify a Response in the hooks such as process_response?
e.g. Given:
from flask import Flask, Response
class MyFlask(Flask):
def process_response(self, response):
# edit response data, eg. add "... MORE!", but
# keep eg mimetype, status_code
response.data += "... This is added" # but should I modify `data`?
return response
# or should I:
# return Response(response.data + "... this is also added",
# mimetype=response.mimetype, etc)
app = MyFlask(__name__)
#app.route('/')
def root():
return "abddef"
if __name__ == '__main__':
app.run()
Is it proper to just create a new response each time, or is it canonical to just edit in-place the response parameter and return that modified response?
This may be purely stylistic, but I'm curious – and I haven't noticed anything in my reading that would indicate the preferred way to do this (even though it's probably quite common).
Thanks for reading.
From the Flask.process_response docs:
Can be overridden in order to modify the response object before it's sent to the WSGI server.
The response object is created on flask dispacher mechanism (Flask.full_dispatch_request). So if you want to create response objects under your own way, override Flask.make_reponse. Use Flask.process_response only when the desired modifications can be made using the created response object parameter.
Actually, you can use Flask.process_response to intercept and modify the response this way:
from flask import Flask
import json
import ast
appVersion = 'v1.0.0'
class LocalFlask(Flask):
def process_response(self, response):
#Every response will be processed here first
response.headers['App-Version'] = appVersion
success = True if response.status_code in [ 200, 201, 204 ] else False
message = 'Ok' if success else 'Error'
dict_str = response.data.decode("UTF-8")
dataDict = ast.literal_eval(dict_str)
standard_response_data = {
'success': success,
'message': message,
'result': dataDict
}
response.data = json.dumps(standard_response_data)
super(LocalFlask, self).process_response(response)
return response