I'm asked to work on a networking website that is like Twitter. I work with HTML,CSS, Javascript for the client-side and Django for the server-side. I'm trying to link between Javascript and Django using JSON and fetch as I want to create a button in each of the users' profile that upon being clicked by the registered user, it makes the registered follow the profile as it is saved in django database as a model containing the follower(follower) and the user followed(following) but upon clicking on follow button (in user.html) it doesn't save any data in the database
in models.py:
class follow(models.Model):
follower = models.ForeignKey("User",on_delete=models.CASCADE, related_name="follower")
following = models.ForeignKey("User",on_delete=models.CASCADE, related_name="following")
in user.html(contains the script):
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded',function(){
document.querySelectorAll('input').forEach(input =>{
input.addEventListener('click', function(){
console.log(input.id);
let follow_username = input.id
fetch('/follow/'+ follow_id, {
method:'PUT',
body: JSON.stringify({
follow: true
})
})
})
}
)
});
</script>
</head>
<body>
<h2>{{x.username}}</h2>
<form>
{% csrf_token %}
<input type="submit" value="follow" name ="follow" id={{x.username}}>
</form>
</body>
</html>
in urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("follow/<str:name>", views.Users, name="follow")
]
in views.py:
def Users(request, name):
x = User.objects.get(username = name)
if request.method == 'PUT':
data = json.loads(request.body)
if data.get('follow') is not None:
user = request.user
anotherr = User.objects.filter(username = name)
another = User.objects.get(username = anotherr).id
follow.objects.create(follower = user, following = another)
return render (request, "network/user.html",{"x":x})
upon clicking on the follow button that present in user.html, no data is created in the database. so what is the problem?
I'll throw my best guesses on what's happening.. Just some improper syntax and undefined variables
View
another / anotherr no, just use x you're already fetching the user at the top of the view
get_or_create will not allow you to have duplicates / follow someone twice (generally a good idea)
Prints are just debugging, remove after you know it's working
def Users(request, name):
x = User.objects.get(username=name)
if request.method == 'PUT':
print('we\'ve hit put')
data = json.loads(request.body)
if data.get('follow') is not None:
print('in follow')
# Note: [User Obj Itself]
# follower = request.user (is: <User Obj>)
# following = x (is: <User Obj>)
followObj, createdBool = follow.objects.get_or_create(follower=request.user, following=x)
print('followObj', followObj)
print('created?', createdBool)
print('past follow')
print('about to render')
return render (request, "network/user.html",{"x":x})
Template
Idk what follow_id is, just use input.id
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded',function(){
document.querySelectorAll('input').forEach(input =>{
input.addEventListener('click', function(){
// this should be true
console.log(input.id == '{{x.username}}');
console.log('Fetch Url:\t' + '/follow/'+ input.id);
fetch('/follow/'+ input.id, {
method:'PUT',
body: JSON.stringify({
follow: true
})
})
})
}
)
});
</script>
</head>
<body>
<h2>{{x.username}}</h2>
<form>
{% csrf_token %}
<input type="submit" value="follow" name ="follow" id={{x.username}}>
</form>
</body>
</html>
If these don't work, tell me what print or console.log got hit or didn't get hit- that'll really help narrow down the issue even more
Edit
Supposedly this, putting a token in a header, will work if you don't want to put a #csrf_exempt decorator (which might be a good idea tbh)
fetch('/follow/'+ input.id, {
method:'PUT',
headers: { 'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val() },
body: JSON.stringify({
follow: true
})
})
Related
This is my list where i tried to get the value of the of a tag
{% extends 'base.html'%}
{% block body %}
<div onclick="pass()">
{{list1[1]}}
</div>
here i tried to get the value of a tag and have tried to pass info route
<script>
function pass() {
var a = document.getElementsByTagName('a');
var w = console.log(a[0].outerHTML);
window.location = "/info/" + w;
}
</script>
{% endblock %}
but show the following error
Not Found
http://127.0.0.1:5000/info/undefined
this is my info route
#app.route('/info', methods=['GET', 'POST'])
def info():
result = request.get_data
print(result)
return render_template('info.html', result=result)
why it's not displaying any data of within a tag
Thank you in Advance
window.location simply opens an HTML page. window.location = "/info/" + w; creates a route /info/w/ but you've only specified the route /info.
You need to send the a data to the server via a form and use Flask's request object to access it.
html code :
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
$('form#chat').submit(function(event) {
const name = document.getElementById('name').innerHTML;
const text = document.getElementById('words').value;
socket.emit('submit value', {'text':text});
socket.emit('submit name', {'name':name});
return false;
});
});
socket.on('chatting', data => {
var name = (data.name);
var text = (data.text);
const li = document.createElement('li');
li.innerHTML = name+':'+text;
document.querySelector('#lists').append(li);
});
});
</script>
<title>chat room</title>
</head>
<body>
<h1 style="font-family:verdana; font-style:italic;">Chat room!!!</h1>
<hr>
<p id="name">{{name}}</p>
<hr>
<ul id="lists">
</ul>
<hr>
<form id="chat" action="">
<input id="words" autocomplete="on" autofocus placeholder="enter text" type="text">
<input type="submit">
</form>
</body>
python code :
import os
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio = SocketIO(app)
#app.route("/")
def index():
return render_template("name.html")
#app.route("/register", methods=["POST","GET"])
def login():
name=request.form.get("name")
return render_template("index.html", name=name)
#socketio.on("submit value")
def chatting(data):
text=data["text"]
emit("chatting", {"text":text}, broadcast=True)
#socketio.on("submit name")
def name(data):
name=data["name"]
emit("chatting", {"name":name}, broadcast=True)
Hi there, so in the above code I'm unable to print my values of "text" and "name" variable in one single 'li' tag, instead of printing the value of both the variables in a single 'li' tag, the value of "name" variable is displayed as "(entered name) : undefined" in a single 'li' tag and the value of the "text" variable is displayed as "undefined : (entered text)" in another 'li' tag. Please help me solve this issue. Thank you.
This is happening because your socket design is a bit faulty. The name and text values are being returned from separate socket broadcasts while you expect them to be together. I see that your name & text values are available together in html, so you can use same socket emit to send both name and text to your server so that they can be broadcast together as well thereby giving you the desired resut.
You can make following modifications:
html code:
// Remove these 2
// socket.emit('submit value', {'text':text});
// socket.emit('submit name', {'name':name});
// and add this line
socket.emit('submit', {'text':text, 'name': name});
python code:
# Remove following two methods chatting & name
'''
#socketio.on("submit value")
def chatting(data):
text=data["text"]
emit("chatting", {"text":text}, broadcast=True)
#socketio.on("submit name")
def name(data):
name=data["name"]
emit("chatting", {"name":name}, broadcast=True)
'''
# And add
#socketio.on("submit")
def name(data):
name=data["name"]
text=data["text"]
emit("chatting", {"name":name, "text": text}, broadcast=True)
So I have a JQuery function that on click of the current input element will change the value of the button. However, I need to be able to get the value of the objects in the li element, denoted by <%= chats[i].msg %>, so I can update my database with the respective data. However on a console.log of the object it keeps returning [object Object]. Is there any way for me to get the data? I'm new to JQuery and EJS so please point me to any duplicates.
JQuery Code
$(document).ready(function(){
$('form').on('submit', function(){ // hit submit
var item = $('form input');
var chat = {msg: item.val(), likes: ''}; //intially the post will have 0 likes
$.ajax({
type: 'POST',
url: '/',
data: chat,
success: function(data){
//do something with the data via front-end framework, so we can update in reall time
console.log(chat);
console.log('success! yes');
location.reload();
}
});
return false;
});
$('.likebutton').on('click', function(){ // hit like
console.log("like clicked?"); // works
// i need to find a way to connect this with code and make it update???
var curr = $('.display').val();
var item = $(this).val('Liked'); // selects approatiate like button
console.log("item: " + item); // return [object Object]?????
$.ajax({ //do something with the data via front-end framework, so we can update in reall time
type: 'GET',
url: '/',
success: function(err){
// here we will get the data, find it in the data base and update it. Changes will occur
// via a reload, either manually or when they create a post, for now. Later we can update
// the thing in real time.
// on click we must also change the color of the thing too to show that it was liked
console.log('success!');
}
});
return false;
});
});
EJS code
<!DOCTYPE html>
<html>
<head>
</head>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="/assets/chat.js"></script>
<body>
<h1> Ut whispers</h1>
<form action="/" method="POST">
<input type="text" placeholder = "say something" name="msg"> <input type="submit" value="Submit"> <br>
</form>
<ul>
<% for(var i = 0; i < chats.length; i++) { %>
<li class = "display"><%= chats[i].msg %> <input type="button" id = "changeit" class = "likebutton" value = "Like: (<%=chats[i].likes%>)" >
<ul>
<li>replies will go here</li>
</ul>
</li>
<% } %>
</ul>
</body>
</html>
Is it possible to make reload the page when the data is updated?
import socket
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.tcpserver
import os
import tornado.websocket
last_messages = dict()
class TcpClient(object):
client_id = 0
def __init__(self, stream):
super(TcpClient, self).__init__()
TcpClient.client_id += 1
self.id = 'Connection#%d' % TcpClient.client_id
self.stream = stream
self.stream.socket.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.stream.socket.setsockopt(
socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1)
self.stream.set_close_callback(self.on_disconnect)
#tornado.gen.coroutine
def on_disconnect(self):
self.log("disconnected")
yield []
#tornado.gen.coroutine
def dispatch_client(self):
try:
while True:
line = yield self.stream.read_until(b'\n')
text_line = line.decode('utf-8').strip()
last_messages[self.id] = text_line
# UPDATE GUI, webpage HERE
self.log('got |%s|' % text_line)
except tornado.iostream.StreamClosedError:
pass
#tornado.gen.coroutine
def on_connect(self):
raddr = 'closed'
try:
raddr = '%s:%d' % self.stream.socket.getpeername()
except Exception:
pass
self.log('new, %s' % raddr)
yield self.dispatch_client()
def log(self, msg, *args, **kwargs):
print('[%s] %s' % (self.id, msg.format(*args, **kwargs)))
class TcpServer(tornado.tcpserver.TCPServer):
#tornado.gen.coroutine
def handle_stream(self, stream, address):
connection = TcpClient(stream)
yield connection.on_connect()
class LastMessagesHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.render(
'template.html',
sources=last_messages
)
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def on_message(self, message):
print message
self.write(last_messages)
def main():
tcp = TcpServer()
tcp.listen(8008)
print('tcp://localhost:8008')
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), 'templates'),
static_path=os.path.join(os.path.dirname(__file__), 'static'),
debug=True,
)
app = tornado.web.Application(
[
(r'/', LastMessagesHandler),
(r'/ws', WebSocketHandler)
],
**settings
)
app.listen(8009)
print('http://localhost:8009')
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
Here is the template.html
<html>
<head>
<title>TCP server</title>
</head>
<body>
<title>Tornado WebSockets</title>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
jQuery(function($){
if (!("WebSocket" in window)) {
alert("Your browser does not support web sockets");
}else{
setup();
}
function setup() {
var host = "ws://localhost:8008/ws";
var socket = new WebSocket(host);
// event handlers for websocket
if(socket){
socket.onopen = function(){
//alert("connection opened....");
}
socket.onmessage = function(msg){
showServerResponse(msg.data);
}
/*socket.onclose = function(){
//alert("connection closed....");
showServerResponse("The connection has been closed.");
}*/
}else{
console.log("invalid socket");
}
function showServerResponse(txt){
var p = document.createElement('p');
p.innerHTML = txt;
document.getElementById('output').appendChild(p);
}
}
});
</script>
<div>
<ul id="output">
{% for key, value in sources.iteritems() %}
<ul> {{key}} | {{value}} </ul>
{% end %}
</ul>
</div>
</body>
</html>
I need to update Client(webpage) whenever last_messages is changed (smth is removed form the dict, smth has been added to the dict). I can't do this using tornado.websocket. Could you please help? I need to update gui after reading line from the socket (you can find the comment in the code where i need to update the webpage: # UPDATE GUI, webpage HERE). So the socket can be still opened (we are continuing reading lines), but the gui must be updated. Is it possible to do it?
If you really want to reload the page then in your JavaScript you can initiate a call to window.location.reload().
However, this will close the WebSocket connection from the client side.
If you just wish to update the DOM (GUI), then listen to the incoming message on the client and update accordingly.
To observe and track data changes you can do it yourself in JavaScript, or with Object.observe if you're running only on modern Chrome, or with the polyfill, or use an existing tool, like Backbone.js.
Should you choose the latter, I can also recommend a tool to help you with working with WebSockets and Backbone.
Disclaimer: I'm the author of Backbone.WS.
I have a select form that needs to be populated through AJAX. Consider this simple example:
templates/index.html
<!DOCTYPE html>
<html>
<body>
<select id="elements">
</select>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script>
$.ajax({
url: '/get-something/5',
type: "GET",
dataType: "html",
success: function (msg) {
$('#elements').html(msg)
}
})
</script>
</body>
</html>
app.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/get-something/<int:n>')
def get_something(n):
result = ''
for i in xrange(n):
result += "<option value='%s'>%s</option>\n" % (i, i)
return result
if __name__ == '__main__':
app.run(debug=True)
That works just fine and is what I did. But, I was wondering if there exists another (better?) way.
For instance, I'd prefer having a context variable to better separate the view from the controller. Something like this:
app.modified.py
#app.route('/get-something/<int:n>')
def get_something(n):
return dict(elements=xrange(n))
templates/index.modified.html
<select id="elements">
{% for elem in elements %}
<option value='{{ elem }}'>{{ elem }}</option>\n
{% endfor %}
</select>
Unfortunately, that does not work. It says, 'dict' object is not callable.
Any help please? Is this even possible? Notice that I don't want to reload the page, I want the same behavior that you get in the AJAX example.
Using index.modified, you need to do something like this:
return render_template('index.modified.html',
elements=your_dictionary)
Not trying to return a dictionary (which is why you are getting that error). See http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-ii-templates fore more examples
You should be returning render_template('index.modified.html', elements=xrange(n)) since you want the html.
render_template returns a string which you can then encode as JSON. Ex:
data = render_template('index.modified.html', elements=xrange(n))
return json.dumps({'data': data})