Using django signals in channels consumer classes

前端 未结 3 1472
礼貌的吻别
礼貌的吻别 2021-01-31 11:16

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

相关标签:
3条回答
  • 2021-01-31 11:52

    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

    0 讨论(0)
  • 2021-01-31 11:52

    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)]
    
    0 讨论(0)
  • 2021-01-31 12:02

    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:

    • Add the consumer to a Group on startup (probably based on its order ID)
    • Send a message to the Group whenever there's a new OrderOffer with a custom type - e.g. {"type": "order.new_offer", "order_offer_id": 45}
    • Define a handler on the Consumer that handles this - it matches the type name, so in this case it would be def order_new_offer(self, event):
    • In that handler you can then use 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

    0 讨论(0)
提交回复
热议问题