I am trying to develop an auction type system, where a customer makes an order, and then different stores can offer a price for that order.
An interesting part of this
If someone stumbles upon on that, this is the way I solved it in the signals.py
. I have a Job
and need to send its status
to the client every time it changes. This is my signals.py
:
import channels.layers
from asgiref.sync import async_to_sync
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Job
def send_message(event):
'''
Call back function to send message to the browser
'''
message = event['text']
channel_layer = channels.layers.get_channel_layer()
# Send message to WebSocket
async_to_sync(channel_layer.send)(text_data=json.dumps(
message
))
@receiver(post_save, sender=Job, dispatch_uid='update_job_status_listeners')
def update_job_status_listeners(sender, instance, **kwargs):
'''
Sends job status to the browser when a Job is modified
'''
user = instance.owner
group_name = 'job-user-{}'.format(user.username)
message = {
'job_id': instance.id,
'title': instance.title,
'status': instance.status,
'modified': instance.modified.isoformat(),
}
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'send_message',
'text': message
}
)
By the way, I have a Consumer class JobUserConsumer(AsyncWebsocketConsumer)
where I define the groups:
async def connect(self):
user = self.scope["user"]
self.group_name = 'job-user-{}'.format(user.username)
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
The project I used this is here: https://github.com/ornl-ndav/django-remote-submission/tree/master/django_remote_submission
For those who still have problems with web sockets, this could be helpful:
from api.models import Order, OrderOffer
from asgiref.sync import async_to_sync
import channels.layers
from channels.generic.websocket import JsonWebsocketConsumer
from django.db.models import signals
from django.dispatch import receiver
class OrderOfferConsumer(JsonWebsocketConsumer):
def connect(self):
async_to_sync(self.channel_layer.group_add)(
'order_offer_group',
self.channel_name
)
self.accept()
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
'order_offer_group',
self.channel_name
)
self.close()
def receive_json(self, content, **kwargs):
print(f"Received event: {content}")
def events_alarm(self, event):
self.send_json(event['data'])
@staticmethod
@receiver(signals.post_save, sender=OrderOffer)
def order_offer_observer(sender, instance, **kwargs):
layer = channels.layers.get_channel_layer()
async_to_sync(layer.group_send)('order_offer_group', {
'type': 'events.alarm',
'data': {
'text': 'Offer received',
'id': instance.pk
}
})
In urls.py you need to register a new webscoket route:
websocket_urlpatterns = [url(r'^order_offer$', OrderOfferConsumer)]
If you want to talk to the consumer from "outside" - in this case, from a model save method - you'll need to use a Channel Layer to talk to it: http://channels.readthedocs.io/en/latest/topics/channel_layers.html
Essentially, you'll need to:
OrderOffer
with a custom type
- e.g. {"type": "order.new_offer", "order_offer_id": 45}
def order_new_offer(self, event):
self.send
to talk down the socket (and query the database if you need extra info to send to the client you didn't put into the event message).You can see a variant of this in the MultiChat example project: https://github.com/andrewgodwin/channels-examples/tree/master/multichat