I don't know the cause and solution of keyerror - python

I'm a beginner.
What I used was flask and pymongo.
If you press the button, it's "Like". It should be +1, but there is a key error at the bottom.
My python route code:
#app.route('/api/like', methods=['POST'])
def like_movie():
title_receive = request.form['title_give']
movie = db.toytoy.find_one({'title': title_receive})
current_like = movie['like']
new_like = current_like + 1
db.toytoy.update_one({'title': title_receive}, {'$set': {'like': new_like}})
return jsonify({'msg': 'like!'})
This is how I POST from JS
function like_movie(title) {
$.ajax({
type: 'POST',
url: '/api/like',
data: {title_give: title},
success: function (response) {
console.log(response)
alert(response['msg']);
window.location.reload();
}
});
}
I get an exception as below:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'title_give'
What I want is if it's 'like_btn'. If you press the button, it becomes +1.

The base problem in what you did is not respecting Content-type. From front JS, you are making a POST with JSON object. Which makes the request to have a content type of application/json.
In backend code, you use request.form which expects the request to be in the form encoded types (like application/x-www-form-urlencoded, multipart/form-data) etc.
So, you need to read the JSON content in backend, instead of reading from a form which is not available. Like below:
ui_req = request.get_json()
title_receive = ui_req['title_give']
And then parse other structures accordingly.

Related

Alamofire 5 (Beta 6): Parameters of PUT Request do not arrive in Flask-Restful

UPDATE
For me the Problem got fixed as soon as I was putting "encoding: URLEncoding(destination: .queryString)" in my request. Maybe this helps somebody else. link
I struggled the whole day to find the problem in my Alamofire PUT Request or the Flask Restful API. Request like GET, DELETE and POST are working fine with Alamofire, except the PUT Request.
When I'm using PUT Requests in combination with Postman and Flask-Restful everything is also working fine. But as soon as I'm trying to achieve the same Result with Alamofire, I'm not getting any parameters in Flask. I tried to illustrate this in the code examples.
So in short my example illustrates the following:
DELETE Request(Same with GET and POST)
Postman: success
Alamofire: success
PUT Request
Postman: success
Alamofire: failure (parameter dictionary empty in Flask-Restful)
Here is my Python Code [API Server]:
from flask import Flask, request, jsonify
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
class Stackoverflow(Resource):
def delete(self):
print(request.args)
if request.args.get('test-key') is None:
return jsonify({"message": "failure"})
else:
return jsonify({"message": "success"})
def put(self):
print(request.args)
if request.args.get('test-key') is None:
return jsonify({"message": "failure"})
else:
return jsonify({"message": "success"})
api.add_resource(Stackoverflow, '/stackoverflow')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
If I'm using Postman, I get this result (like expected):
Result in Postman
But now I'm trying to do the same with Alamofire in Swift. Same Server, nothing changed.
SWIFT demo Code [IOS APP]:
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view
simplePUTRequest()
simpleDELETERequest()
}
func simplePUTRequest(){
AF.request("http://localhost:5000/stackoverflow", method: .put, parameters: ["test-key":"testvalue"])
.validate(statusCode: 200..<300)
.responseJSON { response in
if let data = response.data {
print("Result PUT Request:")
print(String(data: data, encoding: .utf8)!)
//print(utf8Text)
}else{
}
}
}
func simpleDELETERequest(){
AF.request("http://localhost:5000/stackoverflow", method: .delete, parameters: ["test-key":"testvalue"])
.validate(statusCode: 200..<300)
.responseJSON { response in
if let data = response.data {
print("Result DELETE Request:")
print(String(data: data, encoding: .utf8)!)
//print(utf8Text)
}else{
}
}
}
Xcode Console:
Result PUT Request:
{
"message": "failure"
}
Result DELETE Request:
{
"message": "success"
}
python Console (both Alamofire Requests):
ImmutableMultiDict([])
127.0.0.1 - - [15/Jun/2019 21:17:31] "PUT /stackoverflow HTTP/1.1" 200 -
ImmutableMultiDict([('test-key', 'testvalue')])
127.0.0.1 - - [15/Jun/2019 21:17:31] "DELETE /stackoverflow?test-key=testvalue HTTP/1.1" 200 -
As you can see, I'm getting the success message only while using the DELETE method.
Till now I tried using different encodings like URLEncoding.httpbody and URLEncoding.default, but nothing really helped.
For me it seems like it's a Alamofire/Swift Problem, because in Postman the same request method is working fine.
I would really appreciate your help, because I'm stuck and don't know anything further to do. I hope I didn't misunderstood something essential.
Thank you in advance!
I am currently using the same version AlamoFire, and when I use the PUT method, I use it as follows:
let request = AF.request(url, method: .put, parameters: ["uid": uid],
encoding: JSONEncoding.default, headers: headers)
request.responseJSON(completionHandler: { response in
guard response.error == nil else {
//Handle error
}
if let json = response.value as? [String: Any]
// Handle result.
}
The only difference to your post is that I used the encoding option. You can try to put the option and see what happens.
It looks like your server is expecting your PUT parameters to be URL form encoded into the URL. You may be hitting the version of the request method that uses JSON encoding by default, so adding encoder: URLEncodedFormParameterEncoder.default at the end of your request call should fix that. A future release will make that the default, as it's safe across all request types.
If that's not the issue, I suggest you investigate more closely to see what the differences between the requests may be. Since you control the server you should have easy access to the traffic.

Flask displaying response in cmd but not in view

I am reviewing this repository https://github.com/ibrahim4529/flask-chatbot to get an inspiration and see how a model can be deployed in a chat UI. I am able to do this in Flask but ran across a quirky situation that I am not sure how to resolve. The UI accepts the first initial message that I type, but when I try returning the response, I get an 'undefined' appended to every line that is supposed to be the bot's response. On CMD though, my function is returning response. I've provided screenshots below:
Here is my routing:
#app.route('/')
def hello():
return render_template('index.html')
#app.route('/ask', methods={'POST', 'GET'})
def ask():
if request.method == 'POST':
message = (request.form['messageText'])
bresponse = response(message)
return render_template('Index.html', bresponse=bresponse)
'response' is a function that spits out the response of the model (this is the 'Rrraawwwwg!' response from the input).
Below is the js that I reviewed from the link that I am using:
$('#chatbot-form').submit(function(e) {
e.preventDefault();
var message = $('#messageText').val();
$(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div style = "text-align:right; color : #2EFE2E" class="media-body">' + message + '<hr/></div></div></div></li>');
$.ajax({
type: "POST",
url: "/ask",
data: $(this).serialize(),
success: function(response) {
//console.log(response);
$('#messageText').val('');
var answers = response.answers;
const chatPanel = document.getElementById("chatPanel");
$(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div style = "color : white" class="media-body">' + answers + '<hr/></div></div></div></li>');
$(".fixed-panel").stop().animate({ scrollTop: $(".fixed-panel")[0].scrollHeight}, 1000);
I am not sure what I am missing. I am seeing the response in the cli, but seeing an undefined in the UI.
Thanks in advance!
I finally was able to make this work. I've updated ajax to below:
var answers = response;
$('#messageText').val();
I was also printing the response function, instead of returning it; reason I am seeing the response in the console and not in the view. This was the main culprit.

Django (DRF) not showing CORS POST-ed data (from React frontend)

I am using a React frontend, communicating with a DRF (Django Rest Framework) backend.
Currently both are running on their own development server, so they are running on separate domains.
I am sending data from the frontend to the backend, using the POST method, using Axios to send the request.
The code I use can be seen below.
The problem:
Django doesn't seem to be receiving the POST-ed data.
As you can see in the code below, I try to print out the received data, but this is what I see in the console:
[12/Jun/2018 13:33:17] "OPTIONS /order/create HTTP/1.1" 200 0
request.POST:
<QueryDict: {}>
[12/Jun/2018 13:55:47] "POST /order/create HTTP/1.1" 200 2
(it seems to be normal that the printed information comes first in the console, and the line for that POST request after that. Even though the print statement is executed by the POST request, not by the OPTIONS request. This confused me a bit initially.)
What I have tried already:
I've tried to add the following header:
headers: {'Content-Type': 'application/json'}
I've tried adding:
withCredentials: true
(commented out in my code, at the bottom if this post)
Initially this gave me an error about the response to the preflight request not containing an Access-Control-Allow-Credentials' header with the value of 'true'. And this error I resolved by adding the following:
CORS_ALLOW_CREDENTIALS = True
(from the django-cors-headers Django app, set this in settings.py) https://github.com/ottoyiu/django-cors-headers
and then manually adding the header to my response: 
response['Access-Control-Allow-Credentials'] = 'true'
I have already researched parts about CORS before, but again I have read the page below. It doesn't seem to give an answer.
https://www.html5rocks.com/en/tutorials/cors/
The axios code:
checkoutCart: function(submittedValues, products) {
  console.log("checkoutCart")
  console.log(submittedValues)
  console.log(products)
  // let data = {
  //   formData: submittedValues,
  //   productData: products,
  // }
  return axios({
    method: 'post',
    url: 'http://127.0.0.1:8000/order/create',
    data: {
      formData: submittedValues,
      productData: products,
    },
    headers: {'Content-Type': 'application/json'},
    //withCredentials: true,
  })
    .then(function(response) {
      console.log(response)
    })
    .catch(function(error) {
      console.log("error", error)
    })
}
The Django view:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
#csrf_exempt
def handle_order(request):
    if request.method == 'POST':
        print("request.POST:")
        print(request.POST)
    response = JsonResponse({})
    response['Access-Control-Allow-Credentials'] = 'true'
    return response
You can fetch the post data with:
request.body
After you fetch the post data you need to decode it with
request.body.decode('utf-8')
If you wish to parse this dict of data you can use the json library to convert the string into a iterable dict using:
json.loads(your_post_data)
Heres an entire working snippet:
def handle_order(request):
if request.method == 'POST':
unparsed_json = request.body.decode('utf-8') #Get the data and decode it
postData = loads(unparsed_json) #Convert it to [dict]
for keys in postData:
print(keys) #print all the values in the dict

Identify if Flask request is from JavaScript or not

I want to create a Flask error handler that returns a JSON response if the request was from JavaScript but returns a redirect otherwise. I tried using request.is_xhr, but it is false even for JavaScript requests. How can I check if the request is from JavaScript?
#app.errorhandler(Exception)
def unhandled_exception(error):
if request.is_xhr:
return flask.jsonify(error='yes')
return redirect(url_for('error'))
There is no standard or reliable way to detect if a request comes from a particular source, such as JavaScript.
is_xhr was only true when a certain header was set by some JavaScript libraries, such as jQuery. The header is not sent by most JavaScript. is_xhr has been deprecated for that reason.
You can check the Accept header to see if the client is asking for application/json, but that too is unreliable.
if request.is_xhr or request.accept_mimetypes.accept_json:
return jsonify(...)
return redirect(...)
Answer by #davidism makes sense. is_xhr was only true when a certain header was set by some JavaScript libraries. So, I have set header 'X-Requested-With' to 'XMLHttpRequest' manually in '$httpProvider' config in AngularJs. This ensures that on the back end I will get 'is_xhr' true for AJAX request.
app.config([
'$httpProvider',
function ($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
var interceptor = [
'$q',
'$rootScope',
function ($q, $rootScope) {
'responseError': function (rejection) {
if(rejection.status != undefined && rejection.status != 'undefined') {
window.location.href = '/error';
}
}
};
return service;
}
];
$httpProvider.interceptors.push(interceptor);
}
]);
Rather than tie my app to a non-standard header I added an Accept: header to my Javascript instead:
let req = new XMLHttpRequest();
req.open('POST', location);
// signal back-end to return json instead of rendering a full page:
req.setRequestHeader('Accept', 'application/json');
req.send(…);
And in my Python:
# if an ajax-like request, return json instead of html
if request.accept_mimetypes.best == 'application/json':
log.debug('client prefers json, skipping page render.')
return jsonify(status='errror', detail='…')
This should handle other use cases as they come up.
If the request is from a javascript/jquery code, is it most certainly from a browser so you can check the flask.request.user_agent object which is a instance of werkzeug.useragents.UserAgent to verify that.

Save current html state to file via jQuery $.ajax POST to Flask view

Been at this for hours, giving up and posting in the hopes someone can help me out. Maybe this is a terribly stupid idea but I:
Have an html page that I modify using some jQuery controls (adding classes to a large list of items individually on click, filling in textareas, changing page title, etc..
On click of a save button want to do something like :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: myhtml
});
});
Then on the Python/Flask side:
#app.route('/parse_me', methods=['GET', 'POST'])
def parse_me():
if request.method == "POST":
#determine new filename based on #header id
#save new file with request.form data? request.something data??
#using the new filename
I read over the flask docs, and tried many many variations. I'm thinking my problem primarily lies in how I'm sending & then unpacking the data. The closest I got was using:
data: JSON.stringfy(myhtml)
and then on the python side I could "see" that data by doing
print request.values
inside the parse_me() method. Any help would be appreciated. It seemed like the "simplest" solution to what I want to accomplish: I modify the dom with jQuery then ship off my changes to flask to package them into a new file on the server.
Solved!
changed :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: {html:myhtml} // made into a hash for retrieval
});
});
on the python side :
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data():
# updated: get value for key "html", encode correctly
data = request.form.get("html").encode("utf-8")
# do whatever with the data
return "Data retrieved"
Very easy.. loving Flask!

Categories

Resources