I am sending an Ajax POST request to Django from a Javascript modal. The csrf token is included correctly (after much headache...) but for whatever reason, I cannot 'fetch' the request data in my views.py. I have added some comments in the code to indicate what seems to be working
I have been reading everything I could find on this, but still couldn't find the error so any input would be highly appreciated. Thanks!
Javascript
function getMenuItem(id){
console.log(id); // menuitem id prints correctly
// Open request to get menuitem
const request = new XMLHttpRequest();
request.open('POST', '/menuitem');
// Include csrf token in header so Django will accept the request
const header = "X-CSRFToken"
const token = Cookies.get('csrftoken'); // Using the js-cookie library
console.log(token); // token prints correctly
request.setRequestHeader(header, token);
// Send request
request.send(id);
//Once request is received parse it and insert result in DOM
request.onload = () => {
const received = request.responseText;
console.log(received); // Prints the debug message from Django
const parsed = JSON.parse(received);
document.getElementById('menuItem').innerHTML = parsed;
};
};
views.py
def menuitem(request):
if request.method == 'POST':
id = request.body # I have also tried HttpRequest.body
print(id) # Does not print
menuitem = MenuConfiguration.objects.filter(id=id).all()
menuitem = serializers.serialize('json', menuitem)
menuitem = json.loads(menuitem)
return menuitem
Traceback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/utils/deprecation.py", line 93, in __call__
response = self.process_response(request, response)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/middleware/clickjacking.py", line 26, in process_response
if response.get('X-Frame-Options') is not None:
AttributeError: 'list' object has no attribute 'get'
[11/Apr/2019 06:10:18] "POST /menuitem HTTP/1.1" 500 54835
I made it work with the below adjustments, so now the request is sent correctly and processed by Django and the query result is received by the JS. I had to make a slight hack to remove [] from the response text object in order to enable JSON.parse to process it.
I have a new problem though, which is; getting the values of foreign keys included in the query (i.e. not just the keys as it is the case now). I will post a separate question about that, but please leave a comment if you have a solution for this off the cuff
Javascript
function getMenuItem(id){
console.log(id);
// Open request to get menuitem
const request = new XMLHttpRequest();
request.open('POST', '/menuitem');
// Include csrf token in header so Django will accept the request
const header = "X-CSRFToken";
const token = Cookies.get('csrftoken'); //Using the js-cookies library
request.setRequestHeader(header, token);
// Formdata object to structure data as if submitted from a form
const data = new FormData();
data.append('id', id);
// Send request
request.send(data);
console.log("Request sent");
//Once request is received parse it and insert result in DOM
request.onload = () => {
const received = request.responseText;
console.log("Data as received: " + received);
// Remove [] from response text
removedfirst = received.substring(1);
removedlast = removedfirst.substring(0, removedfirst.length-1);
console.log("Data with [] removed: " + removedlast);
// Parse to JS object
const parsed = JSON.parse(received);
console.log("Output of JSON.parse:");
console.log(parsed);
// Insert value into DOM
document.getElementById('outputField').innerHTML = parsed[0].fields.base;
};
};
views.py
def menuitem(request):
if request.method == 'POST':
# Get product id from request
id = request.POST.get('id')
# Retrieve data for specific product id
menuitem = MenuConfiguration.objects.filter(id=id).all()
# Turn query response into JSON
data = serializers.serialize('json', menuitem)
# Return a HttpResponse containing the JSON data
return HttpResponse(data, content_type='application/json')
Output from JS console:
console logs
There's a few things to address here.
In your Javascript code you should send the id value correctly encoded as a form parameter using the key=val syntax:
request.send("id=" + id);
Then in your view, you should retrieve the value from the POST dictionary:
if request.method == 'POST':
id = POST['id'] # Retrieve the value of the id parameter
Lastly your view must return an HttpResponse. Since you're looking to return JSON, you should pass that into the HttpResponse and set the content_type argument to application/json:
if request.method == 'POST':
id = POST['id']
menuitem = MenuConfiguration.objects.filter(id=id).all()
data = serializers.serialize('json', menuitem)
# Return a HttpResponse containing the JSON data
return HttpResponse(data, content_type='application/json')
Related
I'm making a web application with Flask. The user submits a form which is passed to server. The value is passed to a python script I wrote that checks to see if a username is taken on a number of web forums.
As the results come in they are passed back to the page with AJAX. I want to update the webpage as the data comes in. I understand that websockets is probably more efficient, but this is just for practice and I want to learn how to do it with polling.
main.py:
#app.route('/search', methods=['GET', 'POST'])
def search_form():
#print(request.form)
x = request.form['id']
a = Vbulletin(x)
def result_gen():
return a.reg_ver()
result_gen()
def generate_resp():
with app.app_context():
for text in result_gen():
print(text)
text2 = json.dumps(text)
#print(text2)
yield (text2)
sleep(1)
return app.response_class(generate_resp(), mimetype='application/json')
app.run()
forumsearch.js:
$(document).ready(function(){
$("#submit").on('click',function(e){
e.preventDefault();
req = $.ajax({type: "POST",
url: "/search",
data: { id: $("#searchinput").val()},
});
req.done(function(temp){
var latest = document.getElementById('latest');
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '/search');
xhr.send();
var position = 0;
function handleNewData() {
var messages = xhr.responseText.split('\n');
messages.slice(position, -1).forEach(function(value) {
latest.textContent = value;
var item = document.createElement('li');
item.textContent = value;
output.appendChild(item);
});
position = messages.length - 1;
}
var timer;
timer = setInterval(function() {
handleNewData();
if (xhr.readyState == XMLHttpRequest.DONE) {
clearInterval(timer);
latest.textContent = 'Done';
}
}, 1000);
});
});
});
The issue i'm having is that the results are showing up from the POST requests in the browser when I look at the response in the network tab, but it is not being updated on the webpage. There a couple of things I see that could be the issue, but I am unsure how to proceed.
The first is that the XHR 'GET' request is never sent. The 'POST' request is sent and the response is coming in to the client, but the 'GET' request after it is never sent.
Maybe I'm not understanding polling, but don't i need to make a GET request to poll the server? Or do need to just handle the POST request?
Another problem I see is that response that I'm getting from 'POST' request has a syntax error.
The response is this:
"https://www.bimmerboost.com/register.php , user found!!! I SAID USER FOUND!!!""http://www.vbforums.com/register.php , user found!!! I SAID USER FOUND!!!"
and error in firefox is this:
SyntaxError: JSON.parse: unexpected non-whitespace character after
JSON data at line 1 column 80 of the JSON data
How can I fix this error, and what do I need to change to get the server response to show up in html?
This question already has answers here:
How to replace the entire html webpage with ajax response?
(6 answers)
How to replace innerHTML of a div using jQuery?
(14 answers)
Closed 3 years ago.
In index.html, I'm getting a value stored in localStorage:
<script>
function checkChannel() {
if (localStorage.getItem('channel')) {
const channel = localStorage.getItem('channel');
const request = new XMLHttpRequest();
request.open('POST', '/convert');
// Add data to send with request
const data = new FormData();
data.append('channel', channel);
// Send request
request.send(data);
}
}
</script>
</head>
<body onload="checkChannel()">
.
.
.
This works fine. It is routed to the code below in application.py. The debugger hits the first line in convert() below, so I know it's getting there.
#app.route("/convert", methods=["POST"])
def convert():
channel = request.form.get("channel")
channel_messages = channels_dict[channel]
return render_template("channel.html", channel=channel, channel_messages=channel_messages)
However, channel.html is not being rendered. Instead, it stays on the same page. What am I doing wrong here? I can give more details if needed.
You need to define a request.onload() function. This function will be called after the response is received. so your JS code will be similar to this:
function checkChannel() {
if (localStorage.getItem('channel')) {
const channel = localStorage.getItem('channel');
const request = new XMLHttpRequest();
request.open('POST', '/convert');
// Add data to send with request
const data = new FormData();
data.append('channel', channel);
// Send request
request.send(data);
// 4. This will be called after the response is received
request.onload = function() {
if (request.status != 200) {
// analyze HTTP status of the response
alert(`Error ${request.status}: ${request.statusText}`);
// e.g. 404: Not Found
} else { // show the result
$('body').html(request.response)
}
};
}
}
Please note that you can replace $('body').html('some content') by $('div').html('some content') or $('#some-id').html('some content') based on channel.html file content.
Please find this example.
I am using Ajax to to send a get request from the sever, i am querying and getting a particular product from the product model, i want to return result as JavaScript objects but its return this '{' as i try to access the first value from the responseText[0]. How can i convert data return to js object.
here is my code
views.py
def edit_product(request):
product_id = request.GET.get('product_id')
print('THIS IS PRODUCT ID ', product_id)
product = Product.objects.get(pk=product_id)
data = [
{
'name':product.name,
'brand':product.brand,
'price':product.price,
'description':product.description
}
]
return HttpResponse(data)
ajax.js
function getProductEditId(product_id){
//alert(id)
document.getElementById('gridSystemModalLabel').innerHTML='<b> Edit product </b>';
//change modal header from Add product to Edit product
var request = new XMLHttpRequest();
request.open('GET', '/edit_product/?product_id=' + product_id, true);
request.onload = function(){
console.log('hello world', product_id)
//var data = request.responseText
var data = JSON.parse(JSON.stringify(request.responseText));
console.log(data[0])
}
request.send();
}
A HTTP response can not contain a dictionary, you can pass data through a specific format, like for example JSON. Django offers some convenience for that with the JsonResponse [Django-doc]:
from django.http import JsonResponse
def edit_product(request):
product_id = request.GET.get('product_id')
print('THIS IS PRODUCT ID ', product_id)
product = Product.objects.get(pk=product_id)
data = [
{
'name':product.name,
'brand':product.brand,
'price':product.price,
'description':product.description
}
]
return JsonResponse(data, safe=False)
But as the code hints, this is not safe, since an array at the root level was a famous JSON hijacking exploit.
It is typically better to just pass a dictionary (so no list), and then the safe=False parameter can be removed. Although it is not really "safe" to then just assume that all security exploits are gone.
Alternatively, you can use json.dumps [Python-doc] (which is more or less what JsonResponse does internally), but then you thus will probably end with duplicate code.
At the JavaScript side, you then only need to parse the JSON blob to a JavaScript object:
//change modal header from Add product to Edit product
var request = new XMLHttpRequest();
request.open('GET', '/edit_product/?product_id=' + product_id, true);
request.onreadystatechange = function() {
console.log('hello world', product_id)
if(this.status == 200 && this.readyState == 4) {
var data = JSON.parse(this.responseText);
console.log(data[0])
}
}
It is also not very clear to me why you encapsulate the data in a list here: if the response always contains one element, it makes more sense to just pass the dictionary.
I have a backend python script where it retrieves the data from the sqlalchemy engine. And I would like to show the data in a search box where you can scroll down the list of data and select it. I read some answers to the similar questions like mine, (use ajax to call python script). But I'm still not clear about this. Here is my python script.
# models.py
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
from sqlalchemy.ext.declarative import declarative_base
import pandas as pd
aURL = URL(drivername='mysql', username='chlee021690', database = 'recommender')
engine = create_engine(aURL, echo=True)
sql_command = 'SELECT product_id FROM bestbuy_data'
results = pd.read_sql(sql = sql_command, con = engine)
Can anybody tell me how to create javscript code to retrieve that results and render it in my form? Thanks.
Step 1: make your script available as a web service. You can use CGI, or you can use one of the cool server frameworks that will run standalone or WSGI like CherryPy, web.py or Flask.
Step 2: make an AJAX call to the URL served by step 1, either manually (look for XmlHttpRequest examples), or easily using jQuery or another framework (jQuery.ajax(), jQuery.get()).
These are two separate tasks, both are well documented on the web. If you have a more specific question, I suggest you ask again, showing what you are stuck on.
There are also many examples for the complete package available ("python ajax example"), for example this.
Your Python server needs to do 2 things:
Serve up the AJAX javascript file itself (via GET)
respond to calls from the web client (via POST).
Also it should be threaded to support multiple simultaneous connections.
Below is an example showing how to do all of the above with the built-in BaseHTTPServer.
JS (put in static/hello.html to serve via Python):
<html><head><meta charset="utf-8"/></head><body>
Hello.
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "/postman", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
value: 'value'
}));
xhr.onload = function() {
console.log("HELLO")
console.log(this.responseText);
var data = JSON.parse(this.responseText);
console.log(data);
}
</script></body></html>
Python server (for testing):
import time, threading, socket, SocketServer, BaseHTTPServer
import os, traceback, sys, json
log_lock = threading.Lock()
log_next_thread_id = 0
# Local log functiondef
def Log(module, msg):
with log_lock:
thread = threading.current_thread().__name__
msg = "%s %s: %s" % (module, thread, msg)
sys.stderr.write(msg + '\n')
def Log_Traceback():
t = traceback.format_exc().strip('\n').split('\n')
if ', in ' in t[-3]:
t[-3] = t[-3].replace(', in','\n***\n*** In') + '(...):'
t[-2] += '\n***'
err = '\n*** '.join(t[-3:]).replace('"','').replace(' File ', '')
err = err.replace(', line',':')
Log("Traceback", '\n'.join(t[:-3]) + '\n\n\n***\n*** ' + err + '\n***\n\n')
os._exit(4)
def Set_Thread_Label(s):
global log_next_thread_id
with log_lock:
threading.current_thread().__name__ = "%d%s" \
% (log_next_thread_id, s)
log_next_thread_id += 1
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
Set_Thread_Label(self.path + "[get]")
try:
Log("HTTP", "PATH='%s'" % self.path)
with open('static' + self.path) as f:
data = f.read()
Log("Static", "DATA='%s'" % data)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(data)
except:
Log_Traceback()
def do_POST(self):
Set_Thread_Label(self.path + "[post]")
try:
length = int(self.headers.getheader('content-length'))
req = self.rfile.read(length)
Log("HTTP", "PATH='%s'" % self.path)
Log("URL", "request data = %s" % req)
req = json.loads(req)
response = {'req': req}
response = json.dumps(response)
Log("URL", "response data = %s" % response)
self.send_response(200)
self.send_header("Content-type", "application/json")
self.send_header("content-length", str(len(response)))
self.end_headers()
self.wfile.write(response)
except:
Log_Traceback()
# Create ONE socket.
addr = ('', 8000)
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr)
sock.listen(5)
# Launch 10 listener threads.
class Thread(threading.Thread):
def __init__(self, i):
threading.Thread.__init__(self)
self.i = i
self.daemon = True
self.start()
def run(self):
httpd = BaseHTTPServer.HTTPServer(addr, Handler, False)
# Prevent the HTTP server from re-binding every handler.
# https://stackoverflow.com/questions/46210672/
httpd.socket = sock
httpd.server_bind = self.server_close = lambda self: None
httpd.serve_forever()
[Thread(i) for i in range(10)]
time.sleep(9e9)
Console log (chrome):
HELLO
hello.html:14 {"req": {"value": "value"}}
hello.html:16
{req: {…}}
req
:
{value: "value"}
__proto__
:
Object
Console log (firefox):
GET
http://XXXXX:8000/hello.html [HTTP/1.0 200 OK 0ms]
POST
XHR
http://XXXXX:8000/postman [HTTP/1.0 200 OK 0ms]
HELLO hello.html:13:3
{"req": {"value": "value"}} hello.html:14:3
Object { req: Object }
Console log (Edge):
HTML1300: Navigation occurred.
hello.html
HTML1527: DOCTYPE expected. Consider adding a valid HTML5 doctype: "<!DOCTYPE html>".
hello.html (1,1)
Current window: XXXXX/hello.html
HELLO
hello.html (13,3)
{"req": {"value": "value"}}
hello.html (14,3)
[object Object]
hello.html (16,3)
{
[functions]: ,
__proto__: { },
req: {
[functions]: ,
__proto__: { },
value: "value"
}
}
Python log:
HTTP 8/postman[post]: PATH='/postman'
URL 8/postman[post]: request data = {"value":"value"}
URL 8/postman[post]: response data = {"req": {"value": "value"}}
Also you can easily add SSL by wrapping the socket before passing it to BaseHTTPServer.
I have an AJAX function to send an argument and retrieve some json object from a python script. I try to send the value from an input text to the script
AJAX code
function ajax_get_json(){
var results = document.getElementById("results");
var hr = new XMLHttpRequest();
var tipo = document.getElementById('tipo').value;
var atributo = " " +tipo;
document.getElementById('texto').innerHTML = atributo;
hr.open("GET", "prov1.py", + atributo, true);
hr.responseType = "JSON";
hr.setRequestHeader("Content-Type", "application/json", true);
hr.onreadystatechange = function() {
if(hr.readyState == 4 && hr.status == 200) {
var data = JSON.parse(hr.responseText);
results.innerHTML = "";
for(var obj in data){
results.innerHTML +="<tr><td>"+ data[obj].id+"</td><td>"+data[obj].nombre+"</td><td>"+data[obj].tipo+"</td></tr>";
}
}
}
hr.send(null);
results.innerHTML = "requesting...";
}
my python script is this
#!/usr/local/bin/python2.7
import sys
import cx_Oracle
import json
import cgi
import cgitb
cgitb.enable()
form = cgi.FieldStorage()
tipo = form.getvalue('tipo')
#print "Content-Type: text/html; charset=utf-8\n\n";
print "Status: 200 OK"
print "Content-type: application/json\n";
#print
lst_proveedores=[]
conn_str = 'user/pass#database'
conn = cx_Oracle.connect(conn_str)
c = conn.cursor()
c.execute(""" select id_proveedor, nombre, tipo from mpc_proveedores where tipo = '%s' """ %tipo)
for row in c:
record1 = {"id":row[0], "nombre":row[1], "tipo":row[2]}
lst_proveedores.append(record1)
json_string = json.dumps(lst_proveedores)
print json_string
conn.close()
In command line the script work's fine (python prov1.py tipo=MMS) and retrieve from the database the data, but when I try to retrieve the data from AJAX the script send me an empty json object. using firebug I check the response and only appear [].
I think the AJAX function have some error, because appear send the argument empty (python prov1 tipo=).
I am new with AJAX and no sure if I am not using properly the functions in AJAX or is in the python script.
If anybody knows a better way to retrieve the data, let me know please
Help please!!!!
Couple things:
Change
var atributo = " " +tipo;
into
var atributo = "?tipo=" + encodeURIComponent(tipo);
You should check your code for SQL injection vulnerabilities
You're sending the Content-Type header when you make a request to the server, even though you have no request body. (GET requests never have request bodies.) Won't break anything, but you should probably take it out.
responseType is part of HTML5, and is not availible in all browsers.
Best of luck!