Bottle POST method - getting query parameters - python

I am trying to send a POST AJAX request to a Bottle server and read query_string parameters.
This works with GET method, but with POST, bottle.request.query_string is empty.
This is with python 3.6.8. Bottle version in 0.12.17
I'm stuck, please advise.
Bottle server:
#!/usr/bin/env python3
import bottle
print(bottle.__version__)
class EnableCors(object):
name = "enable_cors"
api = 2
def apply(self, fn, context):
def _enable_cors(*args, **kwargs):
bottle.response.headers["Access-Control-Allow-Origin"] = "*"
bottle.response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS"
bottle.response.headers["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
if bottle.request.method != "OPTIONS":
return fn(*args, **kwargs)
return _enable_cors
application = bottle.app()
application.install(EnableCors())
#application.route("/api/params", method=['OPTIONS', 'POST'])
def Api_Params():
print('bottle.request.query_string:', bottle.request.query_string)
bottle.run(host='0.0.0.0', port=8080, debug=True, reloader=True)
Test javscript client:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<script>
function test_post_param() {
var data = {'e': 'E', 'f': 'F', 'g': {'aa':'AA', 'bb':'BB'}};
$.ajax({
url: 'http://127.0.0.1:8080/api/params',
method: "POST",
data: "key=a",
// contentType: "text/plain",
success: function (response, textStatus) {
console.debug("test_post_param OK");
console.debug(textStatus);
console.debug(response);
},
error: function (response, textStatus) {
console.debug("test_post_param ERR");
console.debug(textStatus);
console.debug(response);
},
})
}
window.onload = test_post_param;
</script>
</body>
</html>

I put this on all my API endpoints. I am combining the POST form and query encoding into a single dict.
def merge_dicts(*args):
result = {}
for dictionary in args:
result.update(dictionary)
return result
payload = merge_dicts(dict(request.forms), dict(request.query.decode()))
So your code would look like this:
#application.route("/api/params", method=['OPTIONS', 'POST'])
def Api_Params():
payload = merge_dicts(dict(request.forms), dict(request.query.decode()))
print('bottle.request.query_string: {}'.format(payload))

Here is an example sending the data as JSON to a POST route which I have used successfully.
The JQuery AJAX call:
function test_post_param() {
var data = {'e': 'E', 'f': 'F', 'g': {'aa':'AA', 'bb':'BB'}};
$.ajax({
url: 'http://127.0.0.1:8080/api/params',
method: "POST",
data: JSON.stringify({
"key": "a"
}),
cache: false,
contentType: "application/json",
dataType: "json",
success: function(data, status, xhr){
// Your success code
},
error: function(xhr, status, error) {
// Your error code
}
})
};
The Bottle route:
#application.route("/api/params", method=['POST'])
def Api_Params():
key = bottle.request.forms.get("key")
print(key) # This should print 'a'
I prefer from bottle import route, get, post, template, static_file, request as the import statement. This allows the route to be written more simply (in my opinion).
#post("/api/params")
def Api_Params():
key = request.forms.get("key")
print(key) # This should print 'a'

Related

Django: ajax not returning or sending any data in django

i am creating a simple like button with ajax, i have followed the tutorial but it seems, that i am missing something, i am not getting any error either in the console in my django terminal but when i click the button no data get sent, evrything just remains the same way, and this is not what i am expecting, i know i am missing something somewhere and i cannot really tell where this error is coming from.
views.py
#login_required
def like(request):
if request.POST.get("action") == 'post':
result = ""
id = int(request.POST.get('courseid'))
course = get_object_or_404(Course, id=id)
if course.like.filter(id=request.user.id).exists():
course.like.remove(request.user)
course.like_count -= 1
result = course.like_count
course.save()
else:
course.like.add(request.user)
course.like_count += 1
result = course.like_count
course.save()
return JsonResponse({'result': result})
urls.py NOTE:I don't know if i need a slug in this url path
path('like/', views.like, name="like"),
base.html
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
course-detail.html
<li><button id="like-button" value="{{course.id}}">like</button><span id="like-count">{{course.llke_count}}</span></li>
<script type="text/javascript">
$(document).on("click", '#like-button', function(e){
e.preventDefault()
$.ajax({
type: 'POST',
url: '{% url 'course:like' course.slug %}',
data: {
courseid: $('#like-button').val(),
csrfmiddlewaretoken: $("input[name=csrfmiddlewaretoken]").val(),
action: 'post'
},
success: function(json){
document.getElementById("like-count").innerHTML = json['result']
console.log(json)
},
error: function (xhr, errmsg, err)
console.log(xhr)
console.log(errmsg)
console.log(err)
})
})
</script>
this is all the code i have written for the functionality, if there is any other thing to be provided i will update the question
UPDATE AFTER FIRST ANSWER
#####################################################################
Now when i click the like button is does show an visible error but the like count now shows undefined and in my chrome dev tools is shows failed to load response data because this request was redirected
Update your code like this and I've doubt about your like table provide that inside your question.
inside your views.py
#login_required
def like(request):
if request.method == 'POST':
result = ""
course_id = int(request.POST.get('courseid'))
course = get_object_or_404(Course, id=course_id)
if course.like.filter(id=request.user.id).exists():
course.like.remove(request.user)
course.like_count -= 1
result = course.like_count
course.save()
else:
course.like.add(request.user)
course.like_count += 1
result = course.like_count
course.save()
return JsonResponse({'result': result})
inside your course-detail.html
<script type="text/javascript">
$("#like-button").on("click", function (e) {
e.preventDefault()
$.ajax({
type: 'POST',
url: "{% url 'course:like' %}",
data: {
courseid: $('#like-button').val(),
csrfmiddlewaretoken: "{{ csrf_token }}",
},
success: function (json) {
document.getElementById("like-count").innerHTML = json['result']
console.log(json)
},
error: function (xhr, errmsg, err) {
console.log(xhr)
console.log(errmsg)
console.log(err)
}
})
})
</script>
Note :
You don't have to check for action instead you can check for method eg. request.method.
You've provided wrong url inside your ajax call '{% url 'course:like' course.slug %}' it should be '{% url 'course:like' %}' without passing slug.
Do not use id as avariable because it will conflict with python id() function, you can check for all available built-in functions in python here.

How to include JSON and File data together in FastAPI endpoint?

I would like to POST JSON and File data together, as shown in the code below:
fastapi.py
#router.post('/rate')
def users(user_review:schemas.Rate, image123: UploadFile = File(...), db: Session=Depends(get_db)):
print(image123)
schemas.py
class Rate(BaseModel):
id1:int
id2:int
message:Optional[str] = None
rate:conint(ge=1, le=5)
However, when I execute it, it throws the following 422 error:
{
"detail": [
{
"loc": [
"body",
"user_review"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"body",
"image123"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
You can't declare an endpoint that expects both JSON and File/Form data together, as this is not supported by the HTTP protocol, as explained in FastAPI's documentation. When a request includes Form data, it will have the body encoded using application/x-www-form-urlencoded instead of application/json; if File data are included as well, it will have the body encoded using multipart/form-data.
As explained in this answer, there are various methods to send additional data together with uploading Files. The below demonstrates an approach based on Method 4 of the above answer.
Note: You shouldn't name your python script file fastapi.py (as shown in your question), as this would interfere with the library (when using, for example, from fastapi import FastAPI), but rather use some neutral name, such as app.py.
app.py
from fastapi import FastAPI, File, UploadFile, Body
from pydantic import BaseModel, conint
from typing import Optional
import json
app = FastAPI()
class Rate(BaseModel):
id1: int
id2: int
message: Optional[str] = None
rate: conint(ge=1, le=5)
#classmethod
def __get_validators__(cls):
yield cls.validate_to_json
#classmethod
def validate_to_json(cls, value):
if isinstance(value, str):
return cls(**json.loads(value))
return value
#app.post("/rate")
def submit(user_review: Rate = Body(...), image: UploadFile = File(...)):
return {"JSON Payload ": user_review, "Image": image.filename}
test.py
import requests
url = 'http://127.0.0.1:8000/rate'
file = {'image': open('image.png','rb')}
data = {'user_review': '{"id1": 1, "id2": 2, "message": "foo", "rate": 5}'}
resp = requests.post(url=url, data=data, files=file)
print(resp.json())
Test with Fetch API (using Jinja2Templates or HTMLResponse)
<html>
<head>
<script type="text/javascript">
function submitData() {
var fileInput = document.getElementById('imageFile');
if (fileInput.files[0]) {
var data = new FormData();
data.append("user_review", JSON.stringify({id1: 1, id2: 2, message: "foo", rate: 5}));
data.append("image", fileInput.files[0]);
fetch('/rate', {
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: data
})
.then(resp => resp.text()) // or, resp.json(), etc.
.then(data => {
document.getElementById("responseArea").innerHTML = data;
})
.catch(error => {
console.error(error);
});
}
}
</script>
</head>
<body>
<input type="file" id="imageFile" name="file"></br></br>
<input type="button" value="Submit" onclick="submitData()">
<div id="responseArea"></div>
</body>
</html>

Python Django Ajax get request not working in def function

I'm trying to pass a variable from my JQuery Ajax object via get method to my views.py file (in django)
I was using a class before and it was working just fine..
views.py working code:
class AjaxHandlerView(View):
def get(self, request):
text = request.GET.get('button_text')
print()
print(text)
print()
if request.is_ajax():
return JsonResponse({'test': 'blah'})
return render(request,'home.html' , {'name': 'Handled by AHV'} )
app urls.py
from django.urls import path
from . import views
from .views import *
urlpatterns = [
path('', AjaxHandlerView.as_view() ),
path('ajaxg' , views.ajaxgtest, name="ajaxg" )
]
jquery ajax functions (ignore the post method)
var test_str = "[0,0]";
$(document).ready(function(){
var csrf = $('input[name=csrfmiddlewaretoken]').val()
$('#jq_btn').click(function(){
$.ajax({
url: '',
type: 'get',
data: {
button_text: test_str
},
success: function(response) {
$("#jq_btn").text(response.test + test_str);
console.log(test_str);
}
});
});
$('#post_btn').click(function() {
$.ajax({
url: '',
type: 'post',
data: {
text: test_str,
csrfmiddlewaretoken: csrf
},
success: function(reponse) {
test_str = reponse.postdata
console.log(test_str);
}
})
});
});
However I wanted to use a specific function for specific methods.. so I tried using a def..
views.py that is not working:
def ajaxgtest(self, request):
text = request.GET.get('button_text')
print(text + "deffer")
if request.is_ajax():
return JsonResponse({'test': 'blah'})
return render(request,'home.html' , {'name': 'Handled by AHV'} )
As for the Jquery code, all I did was edit url: '' to url: 'ajaxg' (which is what I named the view in the urls.py file)
new jq ajax code:
$('#jq_btn').click(function(){
$.ajax({
url: 'ajaxg',
type: 'get',
data: {
button_text: test_str
},
success: function(response) {
$("#jq_btn").text(response.test + test_str);
console.log(test_str);
}
});
});
I'm getting the error code;
TypeError: ajaxgtest() missing 1 required positional argument: 'request'
[01/Jan/2021 01:25:31] "GET /ajaxg?button_text=%5B0%2C0%5D HTTP/1.1" 500 58077
I'm unsure where I need to put a request element, or what not..

Bad request Json to Flask ajax

My Jquery code:
var obj_submit = {
email_p : $($('.form-group').find("input[name='email_p']")[0]).val(),
email_a : $($('.form-group').find("input[name='email_a']")[0]).val()
}
var json_submit = JSON.stringify(obj_submit);
$.ajax({
type: 'GET',
url: url,
contentType: 'application/json',
dataType: 'json',
data: json_submit
My python code:
`#app.route('/admission', methods=["GET", "POST"])
def admission():
form = FormFieldsAdmissao()
print (request.is_json)
content = request.get_json()
print (content)
return 'JSON posted'`
in the browser console I receive: GET [...] 400 (BAD REQUEST)
(the number of questions on the same subject is impressive, it makes me think it was something that should be simpler, since everyone has questions about it)

Error trying to save data in django via ajax(fetch)

I have a model that references other models, I am trying to save data using ajax
Example:
class Friend(models.Model):
name = ...
class Main(models.Model):
name = ....
friend = models.ForeignKey(Friend, on_delete=models.CASCADE)
All body comes from ajax(fetch) request
I have a table (html), and add data to cells, then with the
enter event, send data.
Like this:
input.addEventListener("keyup", function (e) {
//in this scenario I already have the whole row
// get full_row `row_data`
post_ajax = {
method: "POST",
headers: {
"X-CSRFToken": crf_token, // I get it with a regular expression
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
Accept: "application/json",
},
body: JSON.stringify(row_data),
};
fetch("my_url", post_ajax)
.then((res) => res.json())
.catch((error) => console.error("Error:", error))
.then((response) => console.log("Success:", response));
});
My view function
def save_post(request):
if request.is_ajax and request.method == "POST":
body_unicode = request.body.decode('utf-8')
data = json.loads(body_unicode)
print('here the data arrives',data)
# here the data arrives {'name': 'Ale', 'friend_id': 22}
Main.objects.create(name=data['name'], friends=data['friend_id'])
return JsonResponse({"instance": data}, status=200)
return JsonResponse({"error": ""}, status=400)
This is the error
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(cls.__name__, kwarg))
TypeError: Main() got an unexpected keyword argument 'Friends'
Any idea or suggestion?
EDIT:
When you are creating the Main object, try making the "friend" attribute an object, like this:
friend = Friend.objects.get(id=data['friend_id'])
Main.objects.create(name=data['name'], friend=friend)
Also, the main issue appears to be you are calling the column "friends" but it should be "friend" when you are creating the Main object.
This:
Main.objects.create(name=data['name'], friends=data['friend_id'])
Should be:
Main.objects.create(name=data['name'], friend=data['friend_id'])
PREVIOUS ANSWER:
Assuming you are using JQuery in the template to send an AJAX request, since you did not specify.
In your urls.py:
...
path('/api/post_friend/', post_friend_api, name="post_friend_api"),
...
In your template :
<script type="text/javascript">
$("#myBurron").click(function(){
var csrfToken = $( "input[name='csrfmiddlewaretoken']"); // assuming this is a form
var friend_name = $("#friend_name").val();
$.ajax({ url: '{% url 'post_friend_api' %}',
type: "POST",
dataType: "json",
data: {'friend':friend_name, 'csrfmiddlewaretoken':csrfToken.val()},
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
In your views.py:
from django.http import JsonResponse
def post_friend_api(request):
data = {}
if request.POST.get('friend', None) is not None:
friend_name = request.POST.get('post_note')
# save the object and indicate success
data['result'] = True
data['message'] = "Friend saved successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
When you are sending data via POST don't forget to pass along your CSRF token as in the example above. This assumes you have a form on the page you can get it from, otherwise you can use something like this to get it:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
If you don't want to deal with the CSRF token, you can mark the view with the #csrf_exempt decorator and remove the 'csrfmiddlewaretoken' data element from the Ajax call in the template, but it may not be ideal or the most secure. An example of that:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
#csrf_exempt()
def post_note_api(request):
...
If you post more details I can update my answer.

Categories