First time poster here. I am trying to put together a chatroom app using Flask-SocketIO and am having a hard time getting one tiny, but crucial, piece to work. When I call the emit method in the server and set the broadcast=True flag, the expected behavior is that it triggers the respective socket.on events on the client side for all users, including the sender. However, my code apparently only does so for all clients other than the sender. This is contrary to the documentation at https://flask-socketio.readthedocs.io/en/latest/, where it states:
When a message is sent with the broadcast option enabled, all clients connected to the namespace receive it, including the sender. When namespaces are not used, the clients connected to the global namespace receive the message.
I need for this to send the message and trigger the socket.on event for the sender as well as all other clients. Can anyone explain what I'm doing wrong here? Because it seems like such a basic feature that I must be missing something really obvious.
Server code:
import os
import requests
from flask import Flask, jsonify, render_template, request
from flask_socketio import SocketIO, emit, join_room, leave_room
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
socketio = SocketIO(app)
channels = ['a', 'b', 'c', 'testing', 'abc', '123']
ch_msgs = {'a': ['testing', '1234', 'high five'], 'b': ['hello']}
msg_limit = 100
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('add channel')
def add(data):
new_channel = data
channels.append(new_channel)
ch_msgs[new_channel] = list()
emit('announce channel', new_channel, broadcast=True)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
Javascript:
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
document.querySelector('#add_channel').onclick = () => {
const new_channel = document.querySelector('#new_channel').value;
socket.emit('add channel', new_channel);
};
});
socket.on('announce channel', data => {
const li = document.createElement('li');
li.innerHTML = '#' + `${data}`;
$('#channel_list').append(li);
});
});
HTML/CSS snippet:
<h3>Chat Channels</h3>
<br>
<ul id='channel_list' style='list-style-type:none;'>
</ul>
<form class='channel'>
<input type='text' id='new_channel' placeholder='add new channel'><input type='submit' id='add_channel' value='Add'>
</form>
All clients in fact are receiving the message sent, including the sender, but the issue here is that the sender's page is refreshed as soon as the form is submitted, (which is the default behavior of forms) thus resulting in loss of all local data.
In order to prevent this, you need to use event.preventDefault() method. In your case, you may change the event handler for connect event like this:
socket.on('connect', () => {
document.querySelector('#add_channel').onclick = event => {
event.preventDefault();
const textinput = document.querySelector('#new_channel')
const new_channel = textinput.value;
textinput.value = ""; // Clear input after value is saved
socket.emit('add channel', new_channel);
};
});
Related
When the client leaves the current room and joins another room, his messages still sent to the old room, how can I make his message just appear for the current room?
the leave_room and join_room are working good because it notifies the user when he joins a room and notifies users in other room that he left the room, so why the message is sent to all the room?
Server-side code:
import os
from flask import Flask, session, render_template, redirect, url_for, escape, request, flash
from flask_session import Session
from flask_socketio import SocketIO, emit, send, join_room, leave_room
from time import localtime, strftime
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio = SocketIO(app)
ROOMS = ["General", "Testing", "One More"]
#app.route("/", methods=["GET", "POST"])
def index():
return render_template("index.html", rooms=ROOMS)
#socketio.on('message')
def message(data):
# print(f"\n\n{data}\n\n")
send({'msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room']}, broadcast=True)
#socketio.on('join')
def on_join(data):
username = data['username']
room = data['room']
join_room(room)
send({'msg': username + " has joined the " + room + " room."}, room=data['room'])
#socketio.on('leave')
def on_leave(data):
username = data['username']
room = data['room']
leave_room(room)
send({'msg': username + " has left the " + room + " room."}, room=data['room'])
if __name__ == '__main__':
socketio.run(app, debug=True)
client side
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
var username = localStorage.getItem("username");
let active_room = "General";
joinRoom(active_room);
if (!username) {
username = prompt("Please enter username");
localStorage.setItem("username", username);
}
if (username) {
document.querySelector('#name-of-client').innerHTML = username;
}
// send message to the server
document.querySelector('#send-btn').onclick = () => {
socket.send({
'msg': document.querySelector('#input-msg').value,
'username': username,
'room': active_room
});
document.querySelector('#input-msg').value = '';
};
socket.on('message', data => {
const p = document.createElement('p');
const span_username = document.createElement('span');
const span_timestamp = document.createElement('span');
const br = document.createElement('br');
if (data.username) {
span_username.innerHTML = data.username;
span_timestamp.innerHTML = data.time_stamp;
p.innerHTML = span_username.outerHTML + br.outerHTML + data.msg + br.outerHTML + span_timestamp.outerHTML + br.outerHTML;
document.querySelector('#display-msg').append(p);
} else {
printSysMsg(data.msg)
}
});
// room selection
document.querySelectorAll('.select-room').forEach(p => {
p.onclick = () => {
let newRoom = p.innerHTML;
if (newRoom === active_room) {
msg = `You are alread in the ${active_room} room.`;
printSysMsg(msg);
} else {
leaveRoom(active_room);
joinRoom(newRoom);
active_room = newRoom;
}
};
});
// Leave Rooms
function leaveRoom(room) {
socket.emit('leave', {
'username': username,
'room': room
});
}
// Join Room
function joinRoom(room) {
socket.emit('join', {
'username': username,
'room': room
});
// Clear Messages
document.querySelector('#display-msg').innerHTML = "";
}
function printSysMsg(msg) {
const p = document.createElement('p');
p.innerHTML = msg;
document.querySelector('#display-msg').append(p);
}
});
It seems like you are creating a chat app using Sandeep Sudhakaran's YouTube tutorial (I guessed it from the printSysMsg() function).
Your problem
I took a look at your code and found that you used broadcast=True on the following socket event ("message"):
#socketio.on('message')
def message(data):
# print(f"\n\n{data}\n\n")
send({'msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room']}, broadcast=True)
Solution
Instead of broadcasting your message event, try limit it to the room:
send({'msg': data['msg'], 'username': data['username'], 'time_stamp': strftime('%b-%d %I:%M%p', localtime()), 'room': data['room']}, room=data["room"])
Reasoning
Remember on the official Flask-SocketIO documentation that:
When a message is sent with the broadcast option enabled, all clients
connected to the namespace receive it, including the sender.
I read up the concept of namespace and room on Socket.IO documentation:
Within each namespace, you can also define arbitrary channels that
sockets can join and leave.
I assume Flask SocketIO works similarly in concept to Socket.IO. So, since you did not specify a specific namespace for this message event, setting broadcast=True means that "a default global namespace with the name '/' is used," and your messages are sent to everyone in this namespace.
I could be wrong as I just started learning about this 3 days ago, but this means that your broadcasting includes all the rooms that you've created in the namespace '/'. Therefore, join_room and leave_room will not help as all the rooms are still under the same namespace. All of them will receive the message.
In your .js file, you have:
let active_room = "General";
joinRoom(active_room);
right at the top of the file. Your user will be kicked back to "General" every time. Try putting some print statements on your server side and console.log statements on your client side to double check what room you're in.
I want to refresh my web page every time a file is uploaded to the folder.
I have a web service written in flask having the following handler
#app.route('/getlatest/')
def getlatest():
import os
import glob
newset = max(glob.iglob('static/*'),key=os.path.getctime)
Return newest;
This gives me the name of the latest file in the folder.
I have an Ajax call in my JS (client side) to constantly get data from above function.
function GetLatest()
{
$.ajax({
url: "http://localhost:5000/getlatest",
success: function(result)
{
if(previousName != result){
previousName = result;
$("#image").attr("src","/"+previousName);
}
}
});
}
function calling server every second.
(function myLoop (i) {
setTimeout(function () {
GetLatest();
if (--i) myLoop(i);
}, 1000)
})(100);
This Works fine [well almost].
My Question is: Is there any better way to do it[ there must be ]?
i'm open to technology choices what every they may be [node, angualr etc.]
yea you can use websockets(flask-socketio).That will let you to have an open connection between you and server and every time in the folder is a new photo will be displayed on a selected div
http://flask-socketio.readthedocs.io/en/latest/
https://pypi.python.org/pypi/Flask-SocketIO/2.9.1
So here is how i did it.
first of all thanks to
https://blog.miguelgrinberg.com/post/easy-websockets-with-flask-and-gevent
for explaining it perfectly.
What i have learnt reading a couple of blogs.
All communication initiated using http protocol is client server communication, and client is always the initiator. So in this case we have to use a different protocol: Web-sockets which allow you to create a full-duplex (two way) connection.
Here is the server code;
socketio = SocketIO(app, async_mode=async_mode)
thread = None
prevFileName = ""
def background_thread():
prevFileName = ""
while True:
socketio.sleep(0.5)
if(isNewImageFileAdded(prevFileName) == "true"):
prevFileName = getLatestFileName()
socketio.emit('my_response',
{'data': prevFileName, 'count': 0},
namespace='/test');
def getLatestFileName():
return max(glob.iglob('static/*'),key=os.path.getctime)
def isNewImageFileAdded(prevFileName):
latestFileName = max(glob.iglob('static/*'),key=os.path.getctime)
if(prevFileName == latestFileName):
return "false"
else:
return "true"
Creating separate thread to keep the socket open. emit sends the msg to the client from server...
#socketio.on('connect', namespace='/test')
def test_connect():
global thread
if thread is None:
thread = socketio.start_background_task(target=background_thread)
emit('my_response', {'data': 'Connected', 'count': 0})
And here is Client Side.[replaced ajax call with the following code]
var socket = io.connect(location.protocol + '//' + document.domain + ':' +
location.port + namespace);
socket.on('my_response', function(msg) {
setTimeout(function(){ $("#image").attr("src","/"+msg.data)},1000)
});
Please correct me if i am wrong.
I'm creating a desktop application using electron and flask. When I attempt to send a selected path (selected by the user via a folder select dialog. Using electron I am able to get the full path and not something like C:/fake_path. This is because electron provides the full path via .files[0].path) to python using flask, I never receive a response or caught an error (the main.logPython function is never called in either branch following the rq).
However, if I include app.debug = True in the python file to turn on the Flask debugger, it runs as expected and prints out the path. Essentially the python code is not even being reached unless I put it into debug mode.
edit: I know this is true because I've attempted to run python code other than just the return (like creating a new file) and it's never run.
edit #2: It seems this problem is related to the admin.js file. If I attempt to make a similar connection in the main.js file I get the result that I expect, however if I use the same code in the admin.js file I get no response as stated above.
Hopefully I've provided enough information. If you need anything else please let me know.
Sorry for any formatting issues with the JavaScript code, I'm very new to it so I just threw it into dirty markup to clean it up a bit.
calling JavaScript code (admin.js)
const remote = require("electron").remote;
const main = remote.require("./main.js");
var importButton = document.getElementById("importButton");
if (importButton.addEventListener) {
importButton.addEventListener("click", importPackages, false);
} else if (importButton.attachEvent) {
importButton.attachEvent("onclick", importPackages);
}
function importPackages() {
var importDirectory = document.getElementById("importDirectory").files[0].path;
var rq = require('request-promise');
var mainAddr = 'http://localhost:5000';
rq(mainAddr + "/import_packages/" + importDirectory).then(function(htmlString) {
if (htmlString != "false") {
main.logPython(htmlString);
}
}).catch(function(err) {
main.logPython('request not sent');
})
}
python code (database_handler.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, request
import sqlite3
app = Flask(__name__)
# app.debug = True
#app.route("/")
def hello():
return "true"
#app.route("/login/<username>/<password>")
def login(username, password):
conn = sqlite3.connect("hict.sqlite")
cursor = conn.cursor()
cursor.execute("SELECT firstname, lastname, userclass FROM credentials "
"WHERE username=? AND password=? LIMIT 1;",
(username, password))
data = cursor.fetchall()
if len(data) == 0:
return "false"
else:
return "|".join(map(str, data[0]))
#app.route("/import_packages/<path:import_directory>")
def import_packages(import_directory):
return import_directory
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000)
Main JavaScript code (main.js)
const electron = require("electron");
const {app, BrowserWindow} = electron;
app.on('window-all-closed', () => {
app.quit();
})
app.on('ready', () => {
var subpy = require('child_process').spawn('python', ['./database_handler.py']);
//var subpy = require('child_process').spawn('./dist/hello.exe');
var rq = require('request-promise');
var mainAddr = 'http://localhost:5000';
var openWindow = () => {
let win = new BrowserWindow({
width: 1920,
height: 1080
});
win.maximize();
win.setMenu(null);
win.loadURL(`file://${__dirname}/login.html`);
win.on('closed', () => {
win = null;
subpy.kill('SIGINT');
})
}
var startUp = () => {
rq(mainAddr).then(function(htmlString) {
console.log('server started!');
openWindow();
}).catch(function(err) {
console.log('waiting for the server start...');
startUp();
})
}
startUp();
})
exports.logPython = (returnData) => {
console.log(returnData);
}
It looks like you are using admin.js as code in your UI, so it is in a renderer process. Your main.js looks like it is running in your project's main process. I am unsure if it actually works to require main.js in admin.js for this case. Why not try using ipc to send the data from your UI to the main process (main.js)?
Also, to eliminate an obvious answer...
console.log() in the main process (main.js) will print out in your terminal as you expect. console.log() in the renderer process (admin.js) prints out in the dev console. On windows I think you open that with by clicking on your UI and then ctrl+shift+i. So if you didn't know this, it might be working but you don't see it.
I am currently trying to use Tornado's web-socket handlers to update a dashboard every time a certain function is called. here is the handler:
class WebSocketHandler(websocket.WebSocketHandler):
clients = []
def open(self):
logging.info("WEBSOCKET OPEN")
WebSocketHandler.clients.append(self)
def on_message(self, message):
logging.info("message from websocket recieved")
self.write_message("WebSocket connected")
def on_close(self):
logging.info("WEBSOCKET closed")
and here is the client-side script which connects the WebSocket on load:
function WebSocketTest()
{ var ws = 0;
ws = new WebSocket("ws://localhost:8008/WEB");
ws.onopen = function()
{
ws.send("initial connect")
}
ws.onmessage = function (evt)
{
console.log(evt.data)
};
ws.onclose = function()
{
console.log("closed ");
};
}
The websockets connect sucessfully.
i need to call write_message from WebSocketHandler but i'm very confused what the instance of it is? the error i keep running into is that self isn't defined but im not sure what self is exactly? i know that WebSocketHandler gets run whenever the client tries to load ^/WEB$
EDIT: here is my server.py file, i need to call write_message right after the spawn callback call in itercheckers
class Server():
#classmethod
def run(cls):
options.parse_command_line()
# Start web
template_path = os.path.join(os.path.dirname(__file__), 'templates')
jinja2loader = Jinja2Loader(template_path)
kwargs = dict(
template_loader=jinja2loader,
static_path=os.path.join(os.path.dirname(__file__), 'static'),
debug=True,
login_url="/auth/login",
cookie_secret="dn470h8yedWF9j61BJH2aY701i6UUexx"
)
app = web.Application(handlers, **kwargs).listen(
configuration['server']['port'],)
# Reset events
#gen.coroutine
def reset(parent=None):
if parent is None:
parent = configuration
# Reset event happyness
yield events.reset_happy(parent)
# Read last status
data = yield events.get(parent)
# Read and set happy from the last status
happy = (data or {}).get('status', events.STATUS_OK) \
in events.HAPPY
yield events.set_happy(parent, happy)
# Iterate sub-events
for event in parent['events']:
yield reset(event)
ioloop.IOLoop.current().run_sync(reset)
# Start checkers
def itercheckers(parent):
index = 0
for event in parent.get('events', []):
if 'checker' in event:
checker = event['checker']
p, m = checker['class'].rsplit('.', 1)
ioloop.IOLoop.current().spawn_callback(
getattr(importlib.import_module(p), m)(
event=event,
frequency=checker.get('frequency', 1),
params=checker['params']
).run)
index += 1
itercheckers(event)
itercheckers(configuration)
# Start alerts
ioloop.IOLoop.current().run_sync(alerts.reset)
for alert in configuration['alerts']:
p, m = alert['class'].rsplit('.', 1)
ioloop.IOLoop.current().spawn_callback(
getattr(importlib.import_module(p), m)(
alert=alert
).run
)
# Start loop
ioloop.IOLoop.current().start()
First thing first, self keyword is pointed to current websocket client that is handled in the moment. To use tornado websockets you must initialize tornado app
app = web.Application([
(r'/ws', WSHandler), #tells it to redirect ws:// to websocket handler
#Choose different names from defaults because of clarity
])
if __name__ == '__main__':
app.listen(5000) #listen on what port
ioloop.IOLoop.instance().start()
Then you must have WSHandler class you're pointing websockets trafic to
class WSHandler(websocket.WebSocketHandler):
#crossdomain connections allowed
def check_origin(self, origin):
return True
#when websocket connection is opened
def open(self):
print("Client connected ")
def on_close(self):
print("Client disconnected")
def on_message(self,message):
self.write_message(message) #echo back whatever client sent you
So complete app would look like
from tornado import websocket, web, ioloop
clients = []
#whenever you want to broadcast to all connected call this function
def broadcast_message(msg):
global clients
for client in clients:
client.write_message(msg)
class WSHandler(websocket.WebSocketHandler):
#crossdomain connections allowed
def check_origin(self, origin):
return True
#when websocket connection is opened
def open(self):
#here you can add clients to your client list if you want
clients.append(self)
print("Client connected ")
def on_close(self):
clients.remove(self)
print("Client disconnected")
def on_message(self,message):
self.write_message(message) #echo back whatever client sent you
app = web.Application([
(r'/ws', WSHandler), #tells it to redirect ws:// to websocket handler
#Choose different names from defaults because of clarity
])
if __name__ == '__main__':
app.listen(5000) #listen on what port
ioloop.IOLoop.instance().start()
And now to connect to it with js
function WebSocketTest()
{
var ws = new WebSocket("ws://localhost:5000/ws");
ws.onopen = function()
{
ws.send("initial connect")
}
ws.onmessage = function (evt)
{
console.log(evt.data)
};
ws.onclose = function()
{
console.log("closed ");
};
}
I haven't tested it but should work
I'm looking at the following API:
http://wiki.github.com/soundcloud/api/oembed-api
The example they give is
Call:
http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json
Response:
{
"html":"<object height=\"81\" ... ",
"user":"Forss",
"permalink":"http:\/\/soundcloud.com\/forss\/flickermood",
"title":"Flickermood",
"type":"rich",
"provider_url":"http:\/\/soundcloud.com",
"description":"From the Soulhack album...",
"version":1.0,
"user_permalink_url":"http:\/\/soundcloud.com\/forss",
"height":81,
"provider_name":"Soundcloud",
"width":0
}
What do I have to do to get this JSON object from just an URL?
It seems they offer a js option for the format parameter, which will return JSONP. You can retrieve JSONP like so:
function getJSONP(url, success) {
var ud = '_' + +new Date,
script = document.createElement('script'),
head = document.getElementsByTagName('head')[0]
|| document.documentElement;
window[ud] = function(data) {
head.removeChild(script);
success && success(data);
};
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
getJSONP('http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=?', function(data){
console.log(data);
});
A standard http GET request should do it. Then you can use JSON.parse() to make it into a json object.
function Get(yourUrl){
var Httpreq = new XMLHttpRequest(); // a new request
Httpreq.open("GET",yourUrl,false);
Httpreq.send(null);
return Httpreq.responseText;
}
then
var json_obj = JSON.parse(Get(yourUrl));
console.log("this is the author name: "+json_obj.author_name);
that's basically it
In modern-day JS, you can get your JSON data by calling ES6's fetch() on your URL and then using ES7's async/await to "unpack" the Response object from the fetch to get the JSON data like so:
const getJSON = async url => {
const response = await fetch(url);
if(!response.ok) // check if response worked (no 404 errors etc...)
throw new Error(response.statusText);
const data = response.json(); // get JSON from the response
return data; // returns a promise, which resolves to this data value
}
console.log("Fetching data...");
getJSON("https://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json").then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});
The above method can be simplified down to a few lines if you ignore the exception/error handling (usually not recommended as this can lead to unwanted errors):
const getJSON = async url => {
const response = await fetch(url);
return response.json(); // get JSON from the response
}
console.log("Fetching data...");
getJSON("https://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json")
.then(data => console.log(data));
Because the URL isn't on the same domain as your website, you need to use JSONP.
For example: (In jQuery):
$.getJSON(
'http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=?',
function(data) { ... }
);
This works by creating a <script> tag like this one:
<script src="http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=someFunction" type="text/javascript"></script>
Their server then emits Javascript that calls someFunction with the data to retrieve.
`someFunction is an internal callback generated by jQuery that then calls your callback.
DickFeynman's answer is a workable solution for any circumstance in which JQuery is not a good fit, or isn't otherwise necessary. As ComFreek notes, this requires setting the CORS headers on the server-side. If it's your service, and you have a handle on the bigger question of security, then that's entirely feasible.
Here's a listing of a Flask service, setting the CORS headers, grabbing data from a database, responding with JSON, and working happily with DickFeynman's approach on the client-side:
#!/usr/bin/env python
from __future__ import unicode_literals
from flask import Flask, Response, jsonify, redirect, request, url_for
from your_model import *
import os
try:
import simplejson as json;
except ImportError:
import json
try:
from flask.ext.cors import *
except:
from flask_cors import *
app = Flask(__name__)
#app.before_request
def before_request():
try:
# Provided by an object in your_model
app.session = SessionManager.connect()
except:
print "Database connection failed."
#app.teardown_request
def shutdown_session(exception=None):
app.session.close()
# A route with a CORS header, to enable your javascript client to access
# JSON created from a database query.
#app.route('/whatever-data/', methods=['GET', 'OPTIONS'])
#cross_origin(headers=['Content-Type'])
def json_data():
whatever_list = []
results_json = None
try:
# Use SQL Alchemy to select all Whatevers, WHERE size > 0.
whatevers = app.session.query(Whatever).filter(Whatever.size > 0).all()
if whatevers and len(whatevers) > 0:
for whatever in whatevers:
# Each whatever is able to return a serialized version of itself.
# Refer to your_model.
whatever_list.append(whatever.serialize())
# Convert a list to JSON.
results_json = json.dumps(whatever_list)
except SQLAlchemyError as e:
print 'Error {0}'.format(e)
exit(0)
if len(whatevers) < 1 or not results_json:
exit(0)
else:
# Because we used json.dumps(), rather than jsonify(),
# we need to create a Flask Response object, here.
return Response(response=str(results_json), mimetype='application/json')
if __name__ == '__main__':
##NOTE Not suitable for production. As configured,
# your Flask service is in debug mode and publicly accessible.
app.run(debug=True, host='0.0.0.0', port=5001) # http://localhost:5001/
your_model contains the serialization method for your whatever, as well as the database connection manager (which could stand a little refactoring, but suffices to centralize the creation of database sessions, in bigger systems or Model/View/Control architectures). This happens to use postgreSQL, but could just as easily use any server side data store:
#!/usr/bin/env python
# Filename: your_model.py
import time
import psycopg2
import psycopg2.pool
import psycopg2.extras
from psycopg2.extensions import adapt, register_adapter, AsIs
from sqlalchemy import update
from sqlalchemy.orm import *
from sqlalchemy.exc import *
from sqlalchemy.dialects import postgresql
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
class SessionManager(object):
#staticmethod
def connect():
engine = create_engine('postgresql://id:passwd#localhost/mydatabase',
echo = True)
Session = sessionmaker(bind = engine,
autoflush = True,
expire_on_commit = False,
autocommit = False)
session = Session()
return session
#staticmethod
def declareBase():
engine = create_engine('postgresql://id:passwd#localhost/mydatabase', echo=True)
whatever_metadata = MetaData(engine, schema ='public')
Base = declarative_base(metadata=whatever_metadata)
return Base
Base = SessionManager.declareBase()
class Whatever(Base):
"""Create, supply information about, and manage the state of one or more whatever.
"""
__tablename__ = 'whatever'
id = Column(Integer, primary_key=True)
whatever_digest = Column(VARCHAR, unique=True)
best_name = Column(VARCHAR, nullable = True)
whatever_timestamp = Column(BigInteger, default = time.time())
whatever_raw = Column(Numeric(precision = 1000, scale = 0), default = 0.0)
whatever_label = Column(postgresql.VARCHAR, nullable = True)
size = Column(BigInteger, default = 0)
def __init__(self,
whatever_digest = '',
best_name = '',
whatever_timestamp = 0,
whatever_raw = 0,
whatever_label = '',
size = 0):
self.whatever_digest = whatever_digest
self.best_name = best_name
self.whatever_timestamp = whatever_timestamp
self.whatever_raw = whatever_raw
self.whatever_label = whatever_label
# Serialize one way or another, just handle appropriately in the client.
def serialize(self):
return {
'best_name' :self.best_name,
'whatever_label':self.whatever_label,
'size' :self.size,
}
In retrospect, I might have serialized the whatever objects as lists, rather than a Python dict, which might have simplified their processing in the Flask service, and I might have separated concerns better in the Flask implementation (The database call probably shouldn't be built-in the the route handler), but you can improve on this, once you have a working solution in your own development environment.
Also, I'm not suggesting people avoid JQuery. But, if JQuery's not in the picture, for one reason or another, this approach seems like a reasonable alternative.
It works, in any case.
Here's my implementation of DickFeynman's approach, in the the client:
<script type="text/javascript">
var addr = "dev.yourserver.yourorg.tld"
var port = "5001"
function Get(whateverUrl){
var Httpreq = new XMLHttpRequest(); // a new request
Httpreq.open("GET",whateverUrl,false);
Httpreq.send(null);
return Httpreq.responseText;
}
var whatever_list_obj = JSON.parse(Get("http://" + addr + ":" + port + "/whatever-data/"));
whatever_qty = whatever_list_obj.length;
for (var i = 0; i < whatever_qty; i++) {
console.log(whatever_list_obj[i].best_name);
}
</script>
I'm not going to list my console output, but I'm looking at a long list of whatever.best_name strings.
More to the point: The whatever_list_obj is available for use in my javascript namespace, for whatever I care to do with it, ...which might include generating graphics with D3.js, mapping with OpenLayers or CesiumJS, or calculating some intermediate values which have no particular need to live in my DOM.
You make a bog standard HTTP GET Request. You get a bog standard HTTP Response with an application/json content type and a JSON document as the body. You then parse this.
Since you have tagged this 'JavaScript' (I assume you mean "from a web page in a browser"), and I assume this is a third party service, you're stuck. You can't fetch data from remote URI in JavaScript unless explicit workarounds (such as JSONP) are put in place.
Oh wait, reading the documentation you linked to - JSONP is available, but you must say 'js' not 'json' and specify a callback: format=js&callback=foo
Then you can just define the callback function:
function foo(myData) {
// do stuff with myData
}
And then load the data:
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = theUrlForTheApi;
document.body.appendChild(script);