20đź‘Ť
Expanding on @Flip’s answer of creating a group for that particular user.
In your python function in your ws_connect function you can add that user into a a group just for them:
consumers.py
from channels.auth import channel_session_user_from_http
from channels import Group
@channel_session_user_from_http
def ws_connect(message):
if user.is_authenticated:
Group("user-{}".format(user.id)).add(message.reply_channel)
To send that user a message from your python code:
my view.py
import json
from channels import Group
def foo(user):
if user.is_authenticated:
Group("user-{}".format(user.id)).send({
"text": json.dumps({
"foo": 'bar'
})
})
If they are connected they will receive the message. If the user is not connected to a websocket it will fail silently.
You will need to also ensure that you only connect one user to each user’s Group, otherwise multiple users could receive a message that you intended for only a specific user.
Have a look at django channels examples, particularly multichat for how to implement routing, creating the websocket connection on the client side and setting up django_channels.
Make sure you also have a look at the django channels docs.
25đź‘Ť
Little update since Groups work differently with channels 2 than they did with channels 1. There is no Group class anymore, as mentioned here.
The new groups API is documented here. See also here.
What works for me is:
# Required for channel communication
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def send_channel_message(group_name, message):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'{}'.format(group_name),
{
'type': 'channel_message',
'message': message
}
)
Do not forget to define a method to handle the message type in the Consumer!
# Receive message from the group
def channel_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
13đź‘Ť
In Channels 2, you can save self.channel_name
in a db on connect method that is a specific hash for each user. Documentation here
from asgiref.sync import async_to_sync
from channels.generic.websocket import AsyncJsonWebsocketConsumer
import json
class Consumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.room_group_name = 'room'
if self.scope["user"].is_anonymous:
# Reject the connection
await self.close()
else:
# Accept the connection
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
print( self.channel_name )
Last line returns something like specific.WxuYsxLK!owndoeYTkLBw
This specific hash you can save in user’s table.
- How to create custom groups in django from group
- How to emit SocketIO event on the serverside
- How does this Man-In-The-Middle attack work?
- Installing django 1.5(development version) in virtualenv
- Django template indentation guideline
5đź‘Ť
The best approach is to create the Group for that particular user. When ws_connect you can add that user into Group("%s" % <user>).add(message.reply_channel)
Note: My websocket url is
ws://127.0.0.1:8000/<user>
- Django template indentation guideline
- How can I schedule a Task to execute at a specific time using celery?
- Changing password in Django Admin
2đź‘Ť
Just to extend @luke_aus’s answer, if you are working with ResourceBindings, you can also make it so, that only users “owning” an object retrieve updates for these:
Just like @luke_aus answer we register the user to it’s own group where we can publish actions (update
, create
) etc that should only be visible to that user:
from channels.auth import channel_session_user_from_http,
from channels import Group
@channel_session_user_from_http
def ws_connect(message):
Group("user-%s" % message.user).add(message.reply_channel)
Now we can change the corresponding binding so that it only publishes changes if the bound object belongs to that user, assuming a model like this:
class SomeUserOwnedObject(models.Model):
owner = models.ForeignKey(User)
Now we can bind this model to our user group and all actions (update, create, etc) will only be published to this one user:
class SomeUserOwnedObjectBinding(ResourceBinding):
# your binding might look like this:
model = SomeUserOwnedObject
stream = 'someuserownedobject'
serializer_class = SomeUserOwnedObjectSerializer
queryset = SomeUserOwnedObject.objects.all()
# here's the magic to only publish to this user's group
@classmethod
def group_names(cls, instance, action):
# note that this will also override all other model bindings
# like `someuserownedobject-update` `someuserownedobject-create` etc
return ['user-%s' % instance.owner.pk]
0đź‘Ť
Although it’s late but I have a direct solution for channels 2 i.e using send
instead of group_send
send(self, channel, message)
| Send a message onto a (general or specific) channel.
use it as –
await self.channel_layer.send(
self.channel_name,
{
'type':'bad_request',
'user':user.username,
'message':'Insufficient Amount to Play',
'status':'400'
}
)
handel it –
await self.send(text_data=json.dumps({
'type':event['type'],
'message': event['message'],
'user': event['user'],
'status': event['status']
}))
Thanks
- Django database synchronization for an offline usage
- How to upgrade Django on ubuntu?
- Django: WSGIRequest' object has no attribute 'user' on some pages?
- Create a canonical "parent" product in Django Oscar programmatically