how to make a websocket connection to dynamic url from the javascript? - websocket

I have a all_products_page.html(super admin side) where i'm getting products of all the user.
In this page there is a column named 'status'. on which i'm sending an ajax request to approve or unapprove product.
I want whenever super admin approve or unapprove a product a notification to be send to that particular user.
How can i achieve that, as i can only send one room_name.
models.py
#receiver(post_save, sender=BroadcastNotification)
def notification_handler(sender, instance, created,update_fields, **kwargs):
# if seen field is not updated in DB , i.e if message is created or updated then send notifiction
if created or update_fields is None:
channel_layer = get_channel_layer()
# call group_send function to send notifications
async_to_sync(channel_layer.group_send)(
"notification_" + "admin" if not instance.from_admin
else "notification_" + str(instance.notification_receiver_id),
{
'type': 'send_notification' ,
'message': json.dumps(str(instance.id) + ">" + instance.message + ">"
+ instance.notification_link)
}
)
In models.py i'm calling notification on post save of data
consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
# get user id of notification receiver
self.room_name = self.scope['path'].split('/')[3]
self.room_group_name = 'notification_%s' % self.room_name
print(self.room_group_name)
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from room group
async def send_notification(self, event):
message = json.loads(event['message'])
print("msg",message)
# Send message to WebSocket
await self.send(text_data=json.dumps(message))
main.js
const notificationSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/notification/'
+ roomName
+ '/'
);
//-----------------If any message for websocket , append message to dropdown and update badge number----------
notificationSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
console.log(data);
let notif_id = data.split('>')[0];
let msg = data.split('>')[1];
let link = data.split('>')[2];
if(roomName == "admin")
{
document.getElementById(`notifications-dropdown`).innerHTML
= `<a class="dropdown-item notification_msg_drpdwn" msg_id="${notif_id}"
href="${link}" >${msg}</a>` +"</li><hr class='dropdown-divider'>" +
document.getElementById(`notifications-dropdown`).innerHTML;
document.getElementById(`notification-badge`).innerHTML
= parseInt(document.getElementById(`notification-badge`).innerHTML) + 1;
}
else
{
document.getElementById(`notifications-dropdown_${roomName}`).innerHTML
= `<a class="dropdown-item notification_msg_drpdwn" msg_id="${notif_id}"
href="${link}" >${msg}</a>` +"</li><hr class='dropdown-divider'>" +
document.getElementById(`notifications-dropdown_${roomName}`).innerHTML;
document.getElementById(`notification-badge_${roomName}`).innerHTML
= parseInt(document.getElementById(`notification-badge_${roomName}`).innerHTML) + 1;
}
};

Related

Async To Sync Not sending message to group

I just want to send message to particular group via async to sync,
Here is my consumers file
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.chat_box_name = self.scope["url_route"]["kwargs"]["chat_box_name"]
self.group_name = "chat_%s" % self.chat_box_name
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
# This function receive messages from WebSocket.
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
username = text_data_json["username"]
await self.channel_layer.group_send(
self.group_name,
{
"type": "chatbox_message",
"message": message,
"username": username,
},
)
# Receive message from room group.
async def chatbox_message(self, event):
message = event["message"]
username = event["username"]
#send message and username of sender to websocket
await self.send(
text_data=json.dumps(
{
"message": message,
"username": username,
}
)
)
async def chat_message(self, event):
message = event["message"]
# Send message to WebSocket
await self.send(text_data=json.dumps({"message": message}))
And this is my code for sending message to specific group
import channels.layers
from asgiref.sync import async_to_sync
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("chat_groupone", {"type": "chatbox_message","message":"STOP","username":"admin"})
I don't know what i am doing wrong in this, it returns me none without any error
I am expecting by running this code, group should have recieved the message
Version-
Django- 4.0
channels - 3.0.4
python - 3.9

websocket handshake fails with django channels

EDIT: correction after #Ken4scholars comment below
I have the following consumer which fails right after connecting
consumers.py
from channels.generic.websocket import AsyncJsonWebsocketConsumer
#...
class ListGeneratedTokensByFileConsumer(AsyncJsonWebsocketConsumer):
stop = False
async def websocket_connect(self,event):
await self.accept()
self.stop = False
async def websocket_receive(self,event):
await self.send_json({"text":"received","accept": True})
await self.send_tokens_list()
async def websocket_disconnect(self,event):
self.stop = True
async def send_tokens_list(self):
some_path = "..."
while self.stop == False:
await asyncio.sleep(2)
the_message = {}
if os.path.isfile("some_file.json")):
with open(os.path.join(some_path ,"some_file.json"),'r') as new_tok:
the_message = json.load(new_tok)
if not the_message:
print("waiting...")
else:
await self.send_json(the_message)
await self.close()
It always throws the error: ERR_CONNECTION:RESEST and the websocket disconnects with code 1006. This might seem familiar to recent changes in django-channels but since I am sending a text once the websocket opens and send a message back from the consumer it should do the trick. Or is there something wrong?
routing.py
url(r'^myapp/sub_path/(?P<pk>\d+)/sub_sub_path/',ListGeneratedTokensByFileConsumer)
and the websocket endpoint in js is:
.js
var loc = window.location;
var wsStart = "ws://";
if (loc.protocol == "https:") {
wsStart = "wss://";
}
var endpoint = wsStart + loc.host + loc.pathname + "sub_sub_path" + "/";
for info, with channels-redis==2.3.2, channels==2.3.0, asgiref==3.2.2, daphne==2.3.0, django==2.0.8
If you see something like in django logs:
WebSocket HANDSHAKING /ws/your_route
WebSocket REJECT /ws/your_route
WebSocket DISCONNECT /ws/your_route
And you wrapped websocket router with AllowedHostsOriginValidator in the asgi.py like:
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
URLRouter(
chat_websocket_urlpatterns
))
})
Then you definetely should check the ALLOWED_HOSTS variable in the settings.py, maybe you forgot to put something in there. In my case it was just because I didn't specify my IP address, only localhost.
This is most likely not that the asker looked for, but I thought it may come handy to someone else, as there is not much info about the django channels in this site.

Can't determine the usage of url for websocket

There is a tutorial page I'm following on WebSockets and Django Channels.
Below is the front end code:
var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket(ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname);
URLs.py
urlpatterns = [
url(r'^$', views.about, name='about'),
url(r'^new/$', views.new_room, name='new_room'),
url(r'^(?P<label>[\w-]{,50})/$', views.chat_room, name='chat_room'),
]
views.py
def chat_room(request, label):
# If the room with the given label doesn't exist, automatically create it
# upon first visit (a la etherpad).
room, created = Room.objects.get_or_create(label=label)
# We want to show the last 50 messages, ordered most-recent-last
messages = reversed(room.messages.order_by('-timestamp')[:50])
return render(request, "chat/room.html", {
'room': room,
'messages': messages,
})
Why are we using the following?
"/chat" + window.location.pathname);
I understand that this URL is the location of the WebSocket server. But how does this URL locate that as we did not specify any such pattern anywhere?

Django view with cross-domain Ajax

I have a Django view that I want to integrate with an Ajax call. The call is happening cross domain. I have this code running by itself and making the request cross-domain.
def myview(_request):
response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
return response
I am wondering how to integrate this into my existing view. You will see what I have tried in the code below:
def american_time(request):
#Calling the HTML Source from the URL
sock = urllib.urlopen("http://apps.cbp.gov/bwt/index.asp")
htmlSource = sock.read()
sock.close()
#create a soup object
soup = BeautifulSoup(htmlSource)
#find the tags we need and their content
bridge = soup.findAll('td', limit=215)[194]
tunnel = soup.findAll('td', limit=250)[208]
#new variables to be passed
contents_of_tunnel = tunnel.getText(', ')
contents_of_bridge = bridge.getText(', ')
#check to see if there is a delay for the bridge
if 'no delay' in contents_of_bridge:
time_to_cross_the_bridge = 0
else:
inside_of_bridge = re.split(r', ', contents_of_bridge)
number_inside_of_bridge = inside_of_bridge[1]
list_for_time_to_cross_the_bridge = re.findall(r"\d+", number_inside_of_bridge)
time_to_cross_the_bridge = list_for_time_to_cross_the_bridge[0]
if 'no delay' in contents_of_tunnel:
time_to_cross_the_tunnel = 0
else:
inside_of_tunnel = re.split(r', ', contents_of_tunnel)
number_inside_of_tunnel = inside_of_tunnel[1]
list_for_time_to_cross_the_tunnel = re.findall(r"\d+", number_inside_of_tunnel)
time_to_cross_the_tunnel = list_for_time_to_cross_the_tunnel[0]
response = HttpResponse(json.dumps({"bridge_time": time_to_cross_the_bridge, "tunnel_time": time_to_cross_the_tunnel}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
#finally, return as Ajax
return HttpResponse(response)
AJAX:
$.get( "http://localhost:8000/us", function(json){
$('#timeone').html(json.bridge_time + "min delay");
$('#timetwo').html(json.tunnel_time + "min delay");
})
.fail(function(){
alert('We can\'t get data right now! Please try again later.');
})
.done(function(){
alert('Success!');
});
However, I am still getting the message XMLHttpRequest cannot load http://localhost:8000/us. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. in the console. How can I integrate this header into my view?
Try add this middleware.
How to add see documentation https://docs.djangoproject.com/en/dev/topics/http/middleware/

Ajax and Django render_to_response (How to render to response inside success function)

Currently I am trying to implement a login validation system. I am using ajax so that users can get a response without being redirected to another page. My ajax function sends email and password that user has inputted, and get message in callback function, which can be in three types: email, password, or the actual HttpResponse object. But I have no idea how to render the given http response object using ajax and jquery. Is location.href an option? I am pasting the code below.
In javascript:
function loginSubmit(email, password) {
var d= "email=" + email + "&password=" + password;
$.ajax({
url: "/login",
type: "POST",
dataType: "text",
data: d,
success: function(m) {
if (m == "email") {
$("#emailMessage").html("There is no account associated with this email address.");
$("#emailError").show();
$("#emailError").fadeOut(5000, function() {});
} else if (m == "password") {
$("#emailMessage").html("There is no account associated with this email address.");
$("#emailError").show();
$("#emailError").fadeOut(5000, function() {});
} else {
}
}
});
}
in view function:
def login(request):
json = request.POST
e = json['email']
p = json['password']
u = User.objects.filter(email=e)
if (len(u)):
up = User.objects.filter(email=e, password=p)
if (len(up)):
return render_to_response('profile.html', context_instance=RequestContext(request))
else:
data = "password"
c = RequestContext(request, {'result':data})
t = Template("{{result}}")
datatype=u"application/javascript"
return HttpResponse(t.render(c), datatype)
else:
data = "email"
c = RequestContext(request, {'result':data})
t = Template("{{result}}")
datatype=u"application/javascript"
return HttpResponse(t.render(c), datatype)
p.s. Currently I am using a dummy template and HttpResponse to send data to the ajax success callback function. Is there a more efficient way to accomplish this (send back json data)? I will wait for your replies guys!
from django.contrib.auth import authenticate, login as auth_login
def login(request):
# Use authentication framework to check user's credentials
# http://djangosnippets.org/snippets/1001/ for auth backend
user = authenticate(
email = request.POST['email'],
password = request.POST['password'], )
if user is not None:
# Use Auth framework to login user
auth_login(request, user)
return render_to_response('profile.html',
context_instance=RequestContext(request))
else:
# Return Access Denied
# Never return bad email/bad password. This is information leakage
# and helps hackers determine who uses your platform and their emails.
return HttpResponse("Failed: Bad username or password", status=403)
function loginSubmit(email, password) {
$.ajax({
url: "/login",
type: "POST",
data: {email:email, password:password},
success: function(data) {
var returned_html = $(data);
$("#target_profile_area").clear().append(returned_html);
},
error: function(jqXHR) {
if (jqXHR.statusCode == 403) {
$("#loginMessage").text("Your login details are incorrect");
} else {
$("#loginMessage").text("Error Contacting Server");
}
$("#loginError").show();
$("#loginError").fadeOut(5000, function() {});
}
});
}

Resources