I am still learning to do javascript and django and yesterday I tried to do a simple hello world ajax exercise.
Server logs show that python code is being called but somehow django/python does not return anything when I check the xmlhttp.responseText and responseXML in firebug.
UPDATE: I removed the checking of the http status returned so that code immediately goes to print the output from the server
<html>
<head>
<title>Javascript example 1</title>
<script type="text/javascript">
function doAjax()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
alert("response text: "+xmlhttp.responseText+"\n"
+"response XML: "+ xmlhttp.responseXML);
if (xmlhttp.responseText!="") {
$("thediv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","http://127.0.0.1/test/",true);
xmlhttp.send();
}
function $(element){
return document.getElementById(element);
}
</script>
</head>
<body>
<input type="button" value="click me" onClick=javascript:doAjax()>
<br/><br/>
<div id="thediv">
some test
</div>
</body>
</html>
my views.py
from django.http import HttpResponse
def test(request):
response_string="hello"
return HttpResponse(response_string,mimetype='text/plain')
my urls.py
from django.conf.urls.defaults import *
from project1.views import test
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
(r'^test/$', test)
# Example:
# (r'^project1/', include('project1.foo.urls')),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
# (r'^admin/', include(admin.site.urls)),
)
UPDATE
Here is the code in action
I just tested your code. When I clicked the "click me" button, a request was indeed made to the test view. I was able to confirm this. However, unlike what you said the view is returning the HttpResponse. To verify this yourself, access the http://localhost:8000/test/ url using your web browser. See what happens.
At first blush your problem seems to be JavaScript related. I don't know what exactly is going wrong but I'll try to debug the JS code and see.
Update
I was able to confirm that the error is indeed with the JavaScript that you are using. I found two errors. First:
if (xmlhttp.readyState==4 && xmlhttp.status==0)
Shouldn't the status be 200? So I changed it to:
if (xmlhttp.readyState==4 && xmlhttp.status==200)
Update 2
Found that I missed the $ function.
The problem is that there are two if conditions. When first evaluates to true, the contents of the div are indeed updated to "hello". However the second if (xmlhttp.responseXML!="") also evaluates to true (null is != "", hence) and wipes out the contents of the div.
Its good to use core JavaScript when learning but you should definitely use some framework such as jQuery or Prototype as you progress. Frameworks allow to keep your code concise, develop faster and also insulate you from the cross-browser compatibility issues.
Using jQuery your code would have been something like this:
<html>
<head>
<title>Javascript example 1</title>
<script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js”></script>
<script type="text/javascript">
function doAjax()
{
$.ajax({
url: 'http://localhost:8000/test/',
success: function(data) {
$('#thediv').html(data); //jQuery equivalent of document.getElementById('thediv').innerHTML = data
}
});
}
</script>
</head>
<body>
<input type="button" value="click me" onClick="javascript:doAjax()"/>
<br/><br/>
<div id="thediv">
some test
</div>
</body>
</html>
Since jQuery provides with a default $() function, you do not need to define them in your code in case you use the framework.
Though this answer is slightly off-track, I hope it will be useful to you.
Related
I am working on a web-scraping project. One of the websites I am working with has the data coming from Javascript.
There was a suggestion on one of my earlier questions that I can directly call the Javascript from Python, but I'm not sure how to accomplish this.
For example: If a JavaScript function is defined as: add_2(var,var2)
How would I call that JavaScript function from Python?
Find a JavaScript interpreter that has Python bindings. (Try Rhino? V8? SeaMonkey?). When you have found one, it should come with examples of how to use it from python.
Python itself, however, does not include a JavaScript interpreter.
To interact with JavaScript from Python I use webkit, which is the browser renderer behind Chrome and Safari. There are Python bindings to webkit through Qt. In particular there is a function for executing JavaScript called evaluateJavaScript().
Here is a full example to execute JavaScript and extract the final HTML.
An interesting alternative I discovered recently is the Python bond module, which can be used to communicate with a NodeJs process (v8 engine).
Usage would be very similar to the pyv8 bindings, but you can directly use any NodeJs library without modification, which is a major selling point for me.
Your python code would look like this:
val = js.call('add2', var1, var2)
or even:
add2 = js.callable('add2')
val = add2(var1, var2)
Calling functions though is definitely slower than pyv8, so it greatly depends on your needs. If you need to use an npm package that does a lot of heavy-lifting, bond is great. You can even have more nodejs processes running in parallel.
But if you just need to call a bunch of JS functions (for instance, to have the same validation functions between the browser/backend), pyv8 will definitely be a lot faster.
You can eventually get the JavaScript from the page and execute it through some interpreter (such as v8 or Rhino). However, you can get a good result in a way easier way by using some functional testing tools, such as Selenium or Splinter. These solutions launch a browser and effectively load the page - it can be slow but assures that the expected browser displayed content will be available.
For example, consider the HTML document below:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function addContent(divId) {
var div = document.getElementById(divId);
div.innerHTML = '<em>My content!</em>';
}
</script>
</head>
<body>
<p>The element below will receive content</p>
<div id="mydiv" />
<script type="text/javascript">addContent('mydiv')</script>
</body>
</html>
The script below will use Splinter. Splinter will launch Firefox and after the complete load of the page it will get the content added to a div by JavaScript:
from splinter.browser import Browser
import os.path
browser = Browser()
browser.visit('file://' + os.path.realpath('test.html'))
elements = browser.find_by_css("#mydiv")
div = elements[0]
print div.value
browser.quit()
The result will be the content printed in the stdout.
You might call node through Popen.
My example how to do it
print execute('''function (args) {
var result = 0;
args.map(function (i) {
result += i;
});
return result;
}''', args=[[1, 2, 3, 4, 5]])
Hi so one possible solution would be to use ajax with flask to comunicate between javascript and python. You would run a server with flask and then open the website in a browser. This way you could run javascript functions when the website is created via pythoncode or with a button how it is done in this example.
HTML code:
<html>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function pycall() {
$.getJSON('/pycall', {content: "content from js"},function(data) {
alert(data.result);
});
}
</script>
<button type="button" onclick="pycall()">click me</button>
</html>
Python Code:
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)
def load_file(file_name):
data = None
with open(file_name, 'r') as file:
data = file.read()
return data
#app.route('/pycall')
def pycall():
content = request.args.get('content', 0, type=str)
print("call_received",content)
return jsonify(result="data from python")
#app.route('/')
def index():
return load_file("basic.html")
import webbrowser
print("opening localhost")
url = "http://127.0.0.1:5000/"
webbrowser.open(url)
app.run()
output in python:
call_received content from js
alert in browser:
data from python
This worked for me for simple js file, source:
https://www.geeksforgeeks.org/how-to-run-javascript-from-python/
pip install js2py
pip install temp
file.py
import js2py
eval_res, tempfile = js2py.run_file("scripts/dev/test.js")
tempfile.wish("GeeksforGeeks")
scripts/dev/test.js
function wish(name) {
console.log("Hello, " + name + "!")
}
Did a whole run-down of the different methods recently.
PyQt4
node.js/zombie.js
phantomjs
Phantomjs was the winner hands down, very straightforward with lots of examples.
I have external programs such as ffmpeg and gstreamer running in the background and writing to a log file. I want to display the contents of this log with my Flask application, so that the user can watch the log update, like tail -f job.log would do in the terminal.
I tried to use <object data="/out.log" type="text/plain"> to point at the log file, but that failed to show the data, or the browser told me I needed a plugin.
How can I embed and update the log file in an HTML page?
Use a Flask view to continuously read from the file forever and stream the response. Use JavaScript to read from the stream and update the page. This example sends the entire file, you may want to truncate that at some point to save bandwidth and memory. This example sleeps between reads to reduce cpu load from the endless loop and allow other threads more active time.
from time import sleep
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/stream')
def stream():
def generate():
with open('job.log') as f:
while True:
yield f.read()
sleep(1)
return app.response_class(generate(), mimetype='text/plain')
app.run()
<pre id="output"></pre>
<script>
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('stream') }}');
xhr.send();
setInterval(function() {
output.textContent = xhr.responseText;
}, 1000);
</script>
This is almost the same as this answer, which describes how to stream and parse messages, although reading from an external file forever was novel enough to be it's own answer. The code here is simpler because we don't care about parsing messages or ending the stream, just tailing the file forever.
I am using frontail package from npm.
npm i frontail -g
frontail /var/log/syslog
visit http://127.0.0.1:9001 to view logs
Source: https://github.com/mthenw/frontail
This may not be the exact answer for the question(to embed an html page), but it solves the problem of many users who are looking specifically only for
Display the contents of a log file as it is updated
For me #davidism solution (accepted answer) worked only on Firefox. It didnt work in Chrome, Brave, Vivaldi. Maybe there was some kind of de-sync in backend and frontend loops? I dont know.
Anyway i used far simpler solution, without loop on the backend and javascript loop on frontend. Maybe it's "uglier" and may cause trouble for some very long logs, but at least it works on every browser i use.
#app.route('/stream')
def stream():
with open("job.log", "r") as f:
content = f.read()
# as you see, file is loaded only once, no loop here, (loop is on frontend side)
return app.response_class(content, mimetype='text/plain')
<!DOCTYPE html>
<html>
<head>
<!-- page auto-refresh every 10 seconds -->
<meta http-equiv="refresh" content="10">
<title>Some title</title>
</head>
<body>
<h1>Log file ...</h1>
<script>
// function for adjusting iframe height to log size
function resizeIframe(obj) {
obj.style.height = obj.contentWindow.document.documentElement.scrollHeight + 'px';
}
</script>
<!-- iframe pulls whole file -->
<iframe src="{{ url_for('stream') }}" frameborder="0" style="overflow:hidden;width:100%" width="100%" frameborder="0" scrolling="no" onload="resizeIframe(this)"></iframe>
</body>
</html>
As you see the only javascript code is used to adjust iframe height to current text size.
I'm new to node and am practicing making http requests using the request module. In my script, when the user presses a button I want its callback function to make a request which gets the HTML from a webpage and filters it to get an array of data.
My server request works by itself, but when I try to combine it with HTML nothing seems to happen. My HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="test1.css" />
<script src = "posts.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>
<body>
<p id = "myText">Hello, this is dog</p>
<button onclick="getPosts()">Get Posts</button>
</body>
</html>
and posts.js is this:
var request = require('request');
function getPosts(){
alert('Hello');
var matches = [];
request('https://www.reddit.com/r/TagPro/top/?sort=top&t=all', function (error, response, body) {
// Handle errors properly.
if (error || response.statusCode !== 200) {
return res.writeHead(error ? 500 : response.statusCode);
}
// Accumulate the matches.
var re = /tabindex="1" >(.+?)</g;
var match;
while (match = re.exec(body)) {
matches[matches.length] = match[1];
}
$("#myText").text(JSON.stringify(matches));
});
}
On the button press, "Hello" gets alerted but nothing happens after that it seems. Is this the proper way to link up node with front end or am I approaching this the wrong way?
If you're running this in the browser then the problem is that you cannot use Node packages in the browser without some extra tooling.
If you check your console, you'll probably see something about "require" being undefined.
You should either read up on how to use tooling like Webpack (or Browserify) to make your Node packages available in the browser.
If you want to stay simple, don't use the Node requests library for client-side (browser) code. Just read up on how to make regular Ajax requests using jQuery or the native XMLHttpRequest API.
You can just replace your request call with something like
$.get('http://someurl.com', function (data) { // stuff });
I saw this great API (http://www.dictionaryapi.com/products/api-collegiate-dictionary.htm) by merriam webster that returns an XML file with all the details in it including definitions and pronunciations.
This API requires a key so i registered and got a key for my account.
I am making the request using Javascript(XHR) but the status returned is zero.
Then i googled the error it said that it may be because my request is going from a "file:///" protocol instead of "http://", so i installed LAMP stack on my PC then hosted the file on my localhost server and even then no luck.
Another thread said that i cant make cross domain requests.
Please can you help me. Below is my HTML code from which i call function in my javascript file.
<html>
<head>
<script type="text/javascript" src="context-script.js">
</script>
</head>
<body>
<h1>Merriam Webster</h1>
<div>
<b>To:</b> <span id="to"></span><br />
<b>From:</b> <span id="from"></span><br />
<b>Message:</b> <span id="message"></span><br/>
<b>Sound:</b><span id="sound"></span><br />
</div>
<script>
callOtherDomain();
</script>
</body>
</html>
Below is my JAvascript file context-script.js code:
function callOtherDomain()
{
invocation = new XMLHttpRequest();
var url = 'http://www.dictionaryapi.com/api/v1/references/collegiate/xml/happy?key=8f394b2c-77e8-433d-b599-f3ca87660067';
//url="note.xml";
if(invocation)
{
invocation.open('GET', url, true);
invocation.withCredentials = "true";
invocation.onreadystatechange = handler;
invocation.send();
alert("ref");
}
}
function handler(evtXHR)
{
if (invocation.readyState == 4)
{
alert("erg");
if (invocation.status == 200)
{
var response = invocation.responseXML;
document.getElementById("to").innerHTML=
response.getElementsByTagName("dt")[0].childNodes[0].nodeValue;
document.getElementById("from").innerHTML=
response.getElementsByTagName("dt")[1].childNodes[0].nodeValue;
document.getElementById("message").innerHTML=
response.getElementsByTagName("dt")[2].childNodes[0].nodeValue;
}
else
alert(invocation.status);
}
else
dump("currently the application is at" + invocation.readyState);
}
But when i change the URL to "note.xml" which is locally stored on the localhost code works absolutely fine.
Thanks in advance.
While this question is several years old, I worked with dictionaryapi.com previously and the solution is two-fold:
Your first step to host on a local server was right on (localhost:8000 or http://127.0.0.1:8000). I prefer using the Python SimpleHTTPServer, started in the root directory of the page you're trying to host with whichever CLI tool you're most familiar/comfortable with, py -m http.server.
After that, just complete a jQuery call using ajax, get, or XMLHttpRequest—whichever you prefer. For example:
$.ajax({
url: 'http://www.dictionaryapi.com/api/v1/references/collegiate/xml/[YourWord]?key=[YourKeyHere],
method: "GET"
}).done(function(response){
console.log(response);
});
Html Page:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xhr</title>
</head>
<body>
<script>
var xhr_test = new XMLHttpRequest();
xhr_test.open("GET","xhrtest",true);
xhr_test.send();
alert(xhr_test.responseText);
</script>
</body>
</html>
The main.py file:
import webapp2
from handlers import cookies,pages
application = webapp2.WSGIApplication([
('/xhr',pages.XHR),
('/xhrtest', cookies.XHRTest)
],
debug=True)
The Request handlers:
class XHRTest(webapp2.RequestHandler):
def get(self):
self.response.write('0')
and,
class XHR(webapp2.RequestHandler):
def get(self):
f = open('static/html/xhr.html','r')
self.response.write(f.read())
Now, when I hit upon the url localhost:8080/xhrtest the browser promptly shows the response 0 as the page's content.
Hitting the url localhost:8080/xhr which indirectly hits /xhrtest, pops up an empty string in the alert box (the responseText is an empty string) but checking chrome's response tab under the network tab, I can see that the request's response is 0.
So why is xhr_test.responseText not able to display the same response?
The call to send is asynchronous (you've set the 'async' parameter to true), which means that your alert is happening immediately, before the request finishes.
You should add an event-listener to xhr.onreadystatechange and use the response within that.
Changing the 'true' to 'false' would make this simple example work, but is not a good idea in the general case.
The MDN page on Ajax explains how XMLHttpRequest should be used.