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?
Related
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;
}
};
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.
I am using Geoserver with an app written with OpenLayers 3. The app can download zipped shapefiles using a WFS service, which works unless I make a large (long URL) request. In that case I get a 413 error in Chrome.
Is there a way I can change this setting so that I can make a longer request to Geoserver (or is the problem something else?
Here is the request:
$('#btnDownloadSHP').click(function (e) {
var tostring = '(' + ids.toString() + ')';
var data = {
service: 'WFS',
version: '1.1.0',
request: 'GetFeature',
typename: 'download_layer',
format_options: "filename:" + shapefileName,
srsname: 'EPSG:3857',
outputFormat: 'SHAPE-ZIP',
CQL_FILTER: "id IN " + tostring
}
var parameters = Object.keys(data).map(function (key) {
return key + '=' + data[key]
}).join('&');
var url = "http://" + servername + "/geoserver/wfs?" + parameters;
//make dummy link and download shapefile
var link = document.createElement("a");
link.download = 'Features';
link.href = url;
link.click();
// }
});
That response would be generated by the server that GeoServer is running on rather than GeoServer itself. So depending on which httpd and/or servlet engine you are using you may be able to fix it there.
But the easy answer is to switch from GET to POST.
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/
I'm using the ajax-upload code to do a simple AJAX file upload. The issue I'm coming across is the file isn't showing up on the backend after submitting.
The frontend code is pretty basic:
<div id="image_uploader">Upload More Images</div>
<script type="text/javascript" charset="utf-8">
function createUploader(){
var uploader = new qq.FileUploader({
element: document.getElementById('image_uploader'),
action: '/add/image/1',
debug: true,
onSubmit : function () {
progress.show();
},
onComplete : function () {
progress.hide();
},
onCancel : function () {
progress.hide();
},
});
};
createUploader();
</script>
The backend code (currently in progress) is also pretty basic:
def add_image(request, id):
print request
if request.FILES:
return HttpResponse("{success:true}")
else:
return HttpResponse("{success:false, message:'Unable to find FILES}")
For me, using code from Alex Kuhl, request.GET['qqfile'] had the filename and request.read() (in Django 1.3) returned the data.
request.FILES was only used in a scenario that hasn't yet happened for me. I'm using ajax-upload to talk directly to Photologue, and my code looks something like this:
def save_upload( uploaded, filename, raw_data ):
"""
raw_data: if True, upfile is a HttpRequest object with raw post data
as the file, rather than a Django UploadedFile from request.FILES
"""
try:
filename = os.path.normpath(os.path.join(IMAGE_UPLOAD_PATH, filename))
with BufferedWriter( FileIO( filename, "wb" ) ) as dest:
# if the "advanced" upload, read directly from the HTTP request
# with the Django 1.3 functionality
if raw_data:
(dirName, fileName) = os.path.split(filename)
(fileBaseName, fileExtension)=os.path.splitext(fileName)
#
# right here, if fileBaseName is less than n characters, might want to slap on a date just for fun
#
try:
i_can_has_p = Photo.objects.get(title=fileBaseName)
title = fileBaseName + "_" + str(datetime.datetime.now().strftime("%Y%m%dT%H%M%S"))
except Photo.DoesNotExist:
title = fileBaseName
title_slug = slugify(title)
p = Photo(title=title, title_slug=title_slug)
p.image.save(filename,ContentFile(uploaded.read()))
# if not raw, it was a form upload so read in the normal Django chunks fashion
else:
# TODO: figure out when this gets called, make it work to save into a Photo like above
for c in uploaded.chunks( ):
dest.write( c )
except IOError:
# could not open the file most likely
return False
return True
def ajax_upload( request ):
if request.method == "POST":
# AJAX Upload will pass the filename in the querystring if it is the "advanced" ajax upload
if request.is_ajax( ):
# the file is stored raw in the request
upload = request
is_raw = True
try:
filename = request.GET[ 'qqfile' ]
except KeyError:
return HttpResponseBadRequest( "AJAX request not valid" )
# not an ajax upload, so it was the "basic" iframe version with submission via form
else:
is_raw = False
if len( request.FILES ) == 1:
# FILES is a dictionary in Django but Ajax Upload gives the uploaded file an
# ID based on a random number, so it cannot be guessed here in the code.
# Rather than editing Ajax Upload to pass the ID in the querystring, note that
# each upload is a separate request so FILES should only have one entry.
# Thus, we can just grab the first (and only) value in the dict.
upload = request.FILES.values( )[ 0 ]
else:
raise Http404( "Bad Upload" )
filename = upload.name
# save the file
success = save_upload( upload, filename, is_raw )
# let Ajax Upload know whether we saved it or not
ret_json = { 'success': success, }
return HttpResponse( json.dumps( ret_json ) )
In my case, ajax_upload is the function called by ajax's action: parameter
Andrew Valums has now got a django app at git hub