I started a a project with Django Cookiecutter w/ Docker: https://cookiecutter-django.readthedocs.io/en/latest/
I'm trying to add Channels and follow the tutorial in their docs: https://channels.readthedocs.io/en/stable/tutorial
I added Channels 3.0.4 to requirements.txt, rebuilt the docker container.
I added channels to settings/base.py, and this:
WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"
I updated my config/asgi.py file:
import os
import sys
from pathlib import Path
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from the_pub.chat import routing
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(ROOT_DIR / "the_pub"))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
django_application = get_asgi_application()
from config.websocket import websocket_application # noqa isort:skip
application = ProtocolTypeRouter({
"https": django_application,
"websocket": AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
),
})
async def application(scope, receive, send):
if scope["type"] == "http":
await django_application(scope, receive, send)
elif scope["type"] == "websocket":
await websocket_application(scope, receive, send)
else:
raise NotImplementedError(f"Unknown scope type {scope['type']}")
created a config/websocket.io file
async def websocket_application(scope, receive, send):
while True:
event = await receive()
if event["type"] == "websocket.connect":
await send({"type": "websocket.accept"})
if event["type"] == "websocket.disconnect":
break
if event["type"] == "websocket.receive":
if event["text"] == "ping":
await send({"type": "websocket.send", "text": "pong!"})
views:
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, 'chat/index.html')
def index(request):
return render(request, 'chat/index.html', {})
def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})
routing:
# chat/routing.py
from django.urls import re_path
from the_pub.chat import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
chat/urls:
# chat/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<str:room_name>/', views.room, name='room'),
]
consumer:
# chat/consumers.py
import json
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
self.send(text_data=json.dumps({
'message': message
}))
app.py
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ChatConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'the_pub.chat'
verbose_name= _("Chat")
def ready(self):
try:
import the_pub.users.signals # noqa F401
except ImportError:
pass
template:
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
console.log(data);
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
When I test the websocket in a chrome plugin I can send messages and it logs. When I hit send on the form it does nothing. no console alerts, nothing in docker logs. All it does is clear the text in the text box. I didn't think a third party could check the socket because I wrapped it in an authentication layer, but it's the opposite, my app acts like the javascript to send the message to the socket doesn't exist.
when you install Channels, it says to do 'pip -m install -U channels'. I added channels to the requirements.txt base file and let django cookiecutter run install with the rest of the libraries. did this break it?
Also, I'm running this project has it was set up by cookiecutter, which I guess is wsgi. Is it even possible to use both wsgi and asgi like this or should I be looking at how to run the whole site on asgi?
I get an error in the console "DevTools failed to load source map: Could not load content for /requestProvider.js.map. I normally ignore these errors but this seams suspiciously related to the socket.send() function on triggering an .onmessage.
You should remove the async def application(...) from config/asgi.py since you are defining the application as a variable right above.
If using channels, the config/websocket.py file is not exactly needed, since you're handling websocket connections from consumers.
Make sure channels_redis is installed if you're using Redis for the channel layer.
Your chat/views.py has two index views. Not sure if that's intended, but it will create unnecessary confusion when it doesn't work.
Related
I'm using django channels tutorial and I did whatever there was in the tutorial , and I'm getting error from my Terminal while I click the send button of my message which is:
"GET /ws/chat/lobby/ HTTP/1.1" 404 2232
my routing.py file :
from django.urls import re_path
from .consumers import ChatConsumer
websocket_urlpatterns = \[
re_path('ws/chat/\<room_name\>', ChatConsumer.as_asgi()),
\]
my consumers.py :
import json
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
text_data_dict = json.loads(text_data)
message = text_data_dict['message']
self.send(text_data=json.dumps({
'message': message
}))
views.py :
from django.shortcuts import render
def index(request):
return render(request, "chat/index.html")
def room(request, room_name):
context = {
"room_name": room_name
}
return render(request, 'chat/room.html', context)
urls.py :
from django.urls import path
from .views import index, room
urlpatterns = [
path('', index, name='index'),
path('<str:room_name>/', room, name='room')
]
asgi.py file :
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import Chat.routing
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
Chat.routing.websocket_urlpatterns
)
),
})
and my room.html file :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
How can I solve this problem?
I tried python manage.py runserver and I got:
Not Found: /ws/chat/lobby/
[11/Nov/2022 11:15:36] "GET /ws/chat/lobby/ HTTP/1.1" 404 2232
I'm newbie in Django Channels and I'm trying to follow and recreate this project from Django channels documentation (which is 4 part) :
https://channels.readthedocs.io/en/latest/tutorial/part_1.html#add-the-index-view
as you can see for accessing the project I have to go with STH like this: 192.168.43.175:8000/chat/lobby_room
I'm wondering how to completely omit the path and accessing the project through : 192.168.43.175:8000/
This is my urls.py :
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('chat/', include('chat.urls')),
]
This is my views.py :
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'chat/index.html')
def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})
This is my chat application urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<str:room_name>/', views.room, name='room'),
]
This is my routing.py:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
This is my consumers.py:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_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 WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_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 WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
This is my room.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
I have managed to fix this issue :)
Step 1 :
In views.py :
We have to replace this code :
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'chat/room.html', {
'room_name': 'lobby_room'
})
Step2:
In chat applications urls.py :
replace this code :
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
Step3:
Finally, replace this code in urls.py :
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('chat.urls')),
]
That's it :)
Url paths are defined in urls.py file in django. As you can see in your urls.py file you have added the chat app on chat/ path which means the app will be accessed at 192.168.43.175:8000/chat/.
So to serve the chat app to 192.168.43.175:8000/ url you simply need to replace chat/ with / in your urls.py like this
path('', include('chat.urls'))
I did a chatting part in my web app with django it works perfectly in local but after I deployed it to heroku , it doesn't work:
this is my consumers.py class:
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from django.db.models import Q
from asgiref.sync import sync_to_async
import json
from chat.models import Thread, Message
from users.serializers import UserSerializer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
friend = None
me = self.scope['user'] # logged in user
friend_name = self.scope['url_route']['kwargs']['friend'] # get the username of that user, whoom you want to chat
friend_instance = await sync_to_async(User.objects.get, thread_sensitive=True)(username=friend_name) # get user object of friend
# create a new Thread object if thread of specific chat does not exists, otherwise return the thread
thread = None
try:
thread = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=me) & Q(user2=friend_instance)) | (Q(user1=friend_instance) & Q(user2=me)))
except:
thread = await sync_to_async(Thread.objects.create, thread_sensitive=True)(user1=me, user2=friend_instance)
self.room_name = thread.room_name # room name
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
'''
disconnect the websocket connection.
'''
await self.channel_layer.group_discard (
self.room_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
from_user = text_data_json['user']
to_user = text_data_json['friend']
from_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=from_user['username']) # get user object of friend
to_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=to_user['username']) # get user object of friend
thread_obj = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=from_user_instanse) & Q(user2=to_user_instanse)) | (Q(user1=to_user_instanse) & Q(user2=from_user_instanse)))
message_instane = await sync_to_async(Message.objects.create, thread_sensitive=True)(messag_body=message, from_user=from_user_instanse, to_user=to_user_instanse, thread=thread_obj)
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chatroom_messages',
'message': message_instane.messag_body,
'user': message_instane.from_user
}
)
async def chatroom_messages(self, event):
message = event['message']
user = event['user']
user_serialized_data = UserSerializer(user)
await self.send(text_data=json.dumps({
'message': message,
'user': user_serialized_data.data
}))
in the template this is the connection request part with js:
chatSocket.send(JSON.stringify({ //the error shows in this part that WebSocket is already in CLOSING or CLOSED state
'message': msg,
'user': me,
'friend': friendName
}));
....
/* connection request */
const chatSocket = new WebSocket(
'wss://'
+ window.location.host
+ '/ws/chat/'
+ friendName['username']
+ '/'
);
in my chat app this I have routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<friend>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
in my project I have also routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter ({
'websocket': AuthMiddlewareStack(
URLRouter (
chat.routing.websocket_urlpatterns
)
)
})
and this is actually my asgi.py :
import os
import django #added
#from django.core.asgi import get_asgi_application #removed
from channels.routing import get_default_application # added
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup() #added
application = get_default_application() #added
#application = get_asgi_application() #removed
in my procfile :
release: python manage.py migrate
web: daphne myproject.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker channels --settings=myproject.settings -v2
in my settings.py
ASGI_APPLICATION = "myproject.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
},
},
}
it works perfectly in local but not anymore working after deployement, am I messing something
I am working on django websocket and creating an Echo app but i don't know why i am getting this error
Here is my html code
{% load staticfiles %}
<html>
<head>
<title>Notifier</title>
<script src="{% static '/channels/js/websocketbridge.js' %}" type="text/javascript"></script>
</head>
<body>
<h1>Notifier</h1>
<script>
document.addEventListener('DOMContentLoaded', function() {
const webSocketBridge = new channels.WebSocketBridge();
webSocketBridge.connect('/ws/');
webSocketBridge.listen(function(action, stream) {
console.log("RESPONSE:", action, stream);
})
document.ws = webSocketBridge; /* for debugging */
})
</script>
</body>
</html>
This is my views.py
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home.html"
This is my routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
from notifier.consumers import EchoConsumer
application = ProtocolTypeRouter({
"websocket": URLRouter([
path("ws/", EchoConsumer),
])
})
Tihs is my counsumers.py
from channels.consumer import AsyncConsumer
class EchoConsumer(AsyncConsumer):
async def websocket_connect(self, event):
await self.send({
"type": "websocket.accept"
})
async def websocket_receive(self, event):
# Echo the same received payload
await self.send({
"type": "websocket.send",
"text": event["text"]
})
Guide me with my mistake where and what i am doing wrong in this whole system i do not find any solution on internet i would expect this code to run properly and do an Echo on my browser console.
I am following , django channels 2 chat app tutorial
I am at the part "writing your first consumer", I exactly did what is in tutorial, you can find my code here
But , I am still unable to connect to my web socket! my error trace :
HTTP GET /chat/ji/ 200 [0.05, 127.0.0.1:51247]
2018-06-23 07:51:43,181 - ERROR - ws_protocol - [Failure instance: Traceback: <class 'ValueError'>: No application configured for scope type 'websocket'
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/internet/defer.py:500:errback
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/internet/defer.py:567:_startRunCallbacks
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/internet/defer.py:653:_runCallbacks
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/internet/defer.py:1442:gotResult
--- <exception caught here> ---
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/internet/defer.py:1384:_inlineCallbacks
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/python/failure.py:422:throwExceptionIntoGenerator
/anaconda3/envs/chatapp/lib/python3.6/site-packages/daphne/server.py:186:create_application
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/python/threadpool.py:250:inContext
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/python/threadpool.py:266:<lambda>
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/python/context.py:122:callWithContext
/anaconda3/envs/chatapp/lib/python3.6/site-packages/twisted/python/context.py:85:callWithContext
/anaconda3/envs/chatapp/lib/python3.6/site-packages/channels/staticfiles.py:41:__call__
/anaconda3/envs/chatapp/lib/python3.6/site-packages/channels/routing.py:58:__call__
]
Not Found: /favicon.ico
[2018/06/23 07:51:43] HTTP GET /favicon.ico 404 [0.03, 127.0.0.1:51249]
[2018/06/23 07:51:47] WebSocket DISCONNECT /ws/chat/ji/ [127.0.0.1:51248]
I am unable to get where it's going wrong!
My main django project is chatapp, in that chat is an app.
chatapp/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(URLRouter(chat.routing.websocket_urlpatterns)),
})
chat/routing.py
from django.conf.urls import url
from . import consumers
websocket_urlpatterns = [
url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer),
]
chat/consumers.py
from channels.generic.websocket import WebsocketConsumer
import json
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
self.send(text_data=json.dumps({
'message': message
}))
chat room where I am trying to web socket:
chat/templates/room.html
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
<input id="chat-message-input" type="text" size="100"/><br/>
<input id="chat-message-submit" type="button" value="Send"/>
</body>
<script>
var roomName = {{ room_name_json }};
var chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/chat/' + roomName + '/');
chatSocket.onopen = function(){
alert('Connection open!');
}
chatSocket.onmessage = function(e) {
var data = JSON.parse(e.data);
var message = data['message'];
document.querySelector('#chat-log').value += (message + '\n');
};
chatSocket.onclose = function(e) {
if(e.code == 1006)
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
} };
document.querySelector('#chat-message-submit').onclick = function(e) {
var messageInputDom = document.querySelector('#chat-message-input');
var message = messageInputDom.value;
chatSocket.send({
'message': message
});
messageInputDom.value = '';
};
</script>
</html>
my project structure
If you are using centos updating redis from version 3 default version in centos to a version that is equal or greater than 6 may help. Worked for me.