This package provides Rails Action Cable support to Django Channels.
Bring Rails ActionCable to Django
Please make sure Django channels is already working in Django project before installing this package.
$ pip install django-actioncable
In the asgi.py
, we have code like this
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django_app.core import routing
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_app.settings")
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": URLRouter(routing.urlpatterns)
}
)
Notes:
- The
websocket
protocol would be handled byURLRouter(routing.urlpatterns)
In the routing file, we add below code:
from actioncable import ActionCableConsumer
urlpatterns = [
path("cable", ActionCableConsumer.as_asgi()),
]
Notes:
- So all the Websocket requests sent to
ws://localhost:8000/cable
would be handled byActionCableConsumer
- The
ActionCableConsumer
would then dispatch the request to the corresponding channel class.
We can add below code to the routing file to register the channel class.
from actioncable import ActionCableConsumer, CableChannel, cable_channel_register
@cable_channel_register
class ChatChannel(CableChannel):
def __init__(self, consumer: ActionCableConsumer, identifier_key, params=None):
self.params = params if params else {}
self.identifier_key = identifier_key
self.consumer = consumer
self.group_name = None
async def subscribe(self):
self.group_name = f"chat_{self.params['pk']}"
await self.consumer.subscribe_group(self.group_name, self)
async def unsubscribe(self):
await self.consumer.unsubscribe_group(self.group_name, self)
- We create a
ChatChannel
, which inherits from theCableChannel
. - The
cable_channel_register
decorator would register theChatChannel
class to theActionCableConsumer
. - In the
subscribe
callback method, we get the room pk from theself.params
dict and subscribe the channel to the groupchat_{pk}
. - In the
unsubscribe
callback method, we unsubscribe the channel from the group.
<!DOCTYPE html>
<html>
<head>
<title>ActionCable Example</title>
</head>
<body>
<div id="messages"></div>
<script src="https://cdn.jsdelivr.net/npm/@rails/actioncable@7.1.2/app/assets/javascripts/actioncable.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Create a consumer object to connect to the ActionCable server
const cable = ActionCable.createConsumer();
// Define a channel and its corresponding functions
const channel = cable.subscriptions.create({channel: "ChatChannel", pk: "1"}, {
connected() {
console.log("Connected to the chat channel.");
},
disconnected() {
console.log("Disconnected from the chat channel.");
},
received(data) {
// Display the received message
const messagesDiv = document.getElementById("messages");
const messageDiv = document.createElement("div");
messageDiv.innerText = data;
messagesDiv.appendChild(messageDiv);
}
});
});
</script>
</body>
</html>
Notes:
- We use the
ActionCable.createConsumer()
to create a consumer object to connect to the ActionCable server. - The
channe
isChatChannel
, so theChatChannel
we just created will be used to handle the request. - In the client, we can pass room pk to the
ChatChannel
bypk: "1"
, and in the backend we can get it in theself.params
- In this case, the channel will
subscribe
to thechat_1
group.
Launch Django shell, and run below code:
from actioncable import cable_broadcast
cable_broadcast("chat_1", message="Hello World")
You should be able to see the message appear on the web page.
cable_broadcast
is a wrapper of Django Channel async_to_sync(channel_layer.group_send)
method call, we can use it in Django view or external process such as Celery worker.
The message
value can also be Python dict, and in javascript we can get it in the data
parameter of the received
callback method.