Sending notifications with Django channels












2














I have project on Django wich use Django Channels. I use Django Channel for sending notifications to users who are subscribed to articles changes (adding/editing/deleting comments on article).



So I've chosen this way of realization: every group of channels is an article and when changes happen, script sends notification to relevant groups. My code works correctly but I have some doubts if my choice of way of realization is most appropriate for this task. I need advice what is the best practice in my case?



Solution:



consumers.py



from channels.generic.websocket import AsyncJsonWebsocketConsumer
from channels.db import database_sync_to_async
from project.apps.account.models import UserStatus
from .models import CommentSubscribe


class CommentNotificationConsumer(AsyncJsonWebsocketConsumer):

async def connect(self):
await self.accept()
if not self.scope['user'].is_anonymous:
groups = await database_sync_to_async(self.get_users_subscription)()
await database_sync_to_async(self.change_user_status)(True)
await self.add_subscriptions(groups)

async def add_subscriptions(self, groups):
for group in groups:
await self.channel_layer.group_add(
'article_{0}'.format(group.article_id),
self.channel_name
)

async def receive_json(self, content, **kwargs):
command = content.get('command', None)
article_id = content.get('article_id', None)
if command == 'subscribe':
await self.subscribe(article_id)
elif command == 'unsubscribe':
await self.unsubscribe(article_id)
else:
await self.send_json({
'error': 'unknown command'
})

async def disconnect(self, code):
await database_sync_to_async(self.change_user_status)(False)

async def send_notification(self, action):
await self.send_json(action)

async def subscribe(self, article_id):
await self.channel_layer.group_add(
'article_{0}'.format(article_id),
self.channel_name
)

async def unsubscribe(self, article_id):
await self.channel_layer.group_discard(
'article_{0}'.format(article_id),
self.channel_name
)

def get_users_subscription(self):
return CommentSubscribe.objects.filter(
user=self.scope['user']
)

def change_user_status(self, online):
return UserStatus.objects.filter(
user=self.scope['user']
).update(online=online)


views.py



from .notify import send_comment_notification

class CreateComment(CreateView):

...

def form_valid(self, form):
...
super().form_valid(form)
send_comment_notification('create', article_id)


class UpdateComment(UpdateView):

...

def form_valid(self, form):
...
super().form_valid(form)
send_comment_notification('update', article_id)


class DeleteComment(DeleteView):

...

def delete(self, request, *args, **kwargs):
...
send_comment_notification('delete', article_id)


notify.py



...

def send_comment_notification(action, article_id):
channel_layer = get_channel_layer()
group_name = 'article_{0}'.format(article_id)
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'send.notification',
'data': {
'action': action
}
}
)









share|improve this question









New contributor




KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

























    2














    I have project on Django wich use Django Channels. I use Django Channel for sending notifications to users who are subscribed to articles changes (adding/editing/deleting comments on article).



    So I've chosen this way of realization: every group of channels is an article and when changes happen, script sends notification to relevant groups. My code works correctly but I have some doubts if my choice of way of realization is most appropriate for this task. I need advice what is the best practice in my case?



    Solution:



    consumers.py



    from channels.generic.websocket import AsyncJsonWebsocketConsumer
    from channels.db import database_sync_to_async
    from project.apps.account.models import UserStatus
    from .models import CommentSubscribe


    class CommentNotificationConsumer(AsyncJsonWebsocketConsumer):

    async def connect(self):
    await self.accept()
    if not self.scope['user'].is_anonymous:
    groups = await database_sync_to_async(self.get_users_subscription)()
    await database_sync_to_async(self.change_user_status)(True)
    await self.add_subscriptions(groups)

    async def add_subscriptions(self, groups):
    for group in groups:
    await self.channel_layer.group_add(
    'article_{0}'.format(group.article_id),
    self.channel_name
    )

    async def receive_json(self, content, **kwargs):
    command = content.get('command', None)
    article_id = content.get('article_id', None)
    if command == 'subscribe':
    await self.subscribe(article_id)
    elif command == 'unsubscribe':
    await self.unsubscribe(article_id)
    else:
    await self.send_json({
    'error': 'unknown command'
    })

    async def disconnect(self, code):
    await database_sync_to_async(self.change_user_status)(False)

    async def send_notification(self, action):
    await self.send_json(action)

    async def subscribe(self, article_id):
    await self.channel_layer.group_add(
    'article_{0}'.format(article_id),
    self.channel_name
    )

    async def unsubscribe(self, article_id):
    await self.channel_layer.group_discard(
    'article_{0}'.format(article_id),
    self.channel_name
    )

    def get_users_subscription(self):
    return CommentSubscribe.objects.filter(
    user=self.scope['user']
    )

    def change_user_status(self, online):
    return UserStatus.objects.filter(
    user=self.scope['user']
    ).update(online=online)


    views.py



    from .notify import send_comment_notification

    class CreateComment(CreateView):

    ...

    def form_valid(self, form):
    ...
    super().form_valid(form)
    send_comment_notification('create', article_id)


    class UpdateComment(UpdateView):

    ...

    def form_valid(self, form):
    ...
    super().form_valid(form)
    send_comment_notification('update', article_id)


    class DeleteComment(DeleteView):

    ...

    def delete(self, request, *args, **kwargs):
    ...
    send_comment_notification('delete', article_id)


    notify.py



    ...

    def send_comment_notification(action, article_id):
    channel_layer = get_channel_layer()
    group_name = 'article_{0}'.format(article_id)
    async_to_sync(channel_layer.group_send)(
    group_name,
    {
    'type': 'send.notification',
    'data': {
    'action': action
    }
    }
    )









    share|improve this question









    New contributor




    KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.























      2












      2








      2







      I have project on Django wich use Django Channels. I use Django Channel for sending notifications to users who are subscribed to articles changes (adding/editing/deleting comments on article).



      So I've chosen this way of realization: every group of channels is an article and when changes happen, script sends notification to relevant groups. My code works correctly but I have some doubts if my choice of way of realization is most appropriate for this task. I need advice what is the best practice in my case?



      Solution:



      consumers.py



      from channels.generic.websocket import AsyncJsonWebsocketConsumer
      from channels.db import database_sync_to_async
      from project.apps.account.models import UserStatus
      from .models import CommentSubscribe


      class CommentNotificationConsumer(AsyncJsonWebsocketConsumer):

      async def connect(self):
      await self.accept()
      if not self.scope['user'].is_anonymous:
      groups = await database_sync_to_async(self.get_users_subscription)()
      await database_sync_to_async(self.change_user_status)(True)
      await self.add_subscriptions(groups)

      async def add_subscriptions(self, groups):
      for group in groups:
      await self.channel_layer.group_add(
      'article_{0}'.format(group.article_id),
      self.channel_name
      )

      async def receive_json(self, content, **kwargs):
      command = content.get('command', None)
      article_id = content.get('article_id', None)
      if command == 'subscribe':
      await self.subscribe(article_id)
      elif command == 'unsubscribe':
      await self.unsubscribe(article_id)
      else:
      await self.send_json({
      'error': 'unknown command'
      })

      async def disconnect(self, code):
      await database_sync_to_async(self.change_user_status)(False)

      async def send_notification(self, action):
      await self.send_json(action)

      async def subscribe(self, article_id):
      await self.channel_layer.group_add(
      'article_{0}'.format(article_id),
      self.channel_name
      )

      async def unsubscribe(self, article_id):
      await self.channel_layer.group_discard(
      'article_{0}'.format(article_id),
      self.channel_name
      )

      def get_users_subscription(self):
      return CommentSubscribe.objects.filter(
      user=self.scope['user']
      )

      def change_user_status(self, online):
      return UserStatus.objects.filter(
      user=self.scope['user']
      ).update(online=online)


      views.py



      from .notify import send_comment_notification

      class CreateComment(CreateView):

      ...

      def form_valid(self, form):
      ...
      super().form_valid(form)
      send_comment_notification('create', article_id)


      class UpdateComment(UpdateView):

      ...

      def form_valid(self, form):
      ...
      super().form_valid(form)
      send_comment_notification('update', article_id)


      class DeleteComment(DeleteView):

      ...

      def delete(self, request, *args, **kwargs):
      ...
      send_comment_notification('delete', article_id)


      notify.py



      ...

      def send_comment_notification(action, article_id):
      channel_layer = get_channel_layer()
      group_name = 'article_{0}'.format(article_id)
      async_to_sync(channel_layer.group_send)(
      group_name,
      {
      'type': 'send.notification',
      'data': {
      'action': action
      }
      }
      )









      share|improve this question









      New contributor




      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      I have project on Django wich use Django Channels. I use Django Channel for sending notifications to users who are subscribed to articles changes (adding/editing/deleting comments on article).



      So I've chosen this way of realization: every group of channels is an article and when changes happen, script sends notification to relevant groups. My code works correctly but I have some doubts if my choice of way of realization is most appropriate for this task. I need advice what is the best practice in my case?



      Solution:



      consumers.py



      from channels.generic.websocket import AsyncJsonWebsocketConsumer
      from channels.db import database_sync_to_async
      from project.apps.account.models import UserStatus
      from .models import CommentSubscribe


      class CommentNotificationConsumer(AsyncJsonWebsocketConsumer):

      async def connect(self):
      await self.accept()
      if not self.scope['user'].is_anonymous:
      groups = await database_sync_to_async(self.get_users_subscription)()
      await database_sync_to_async(self.change_user_status)(True)
      await self.add_subscriptions(groups)

      async def add_subscriptions(self, groups):
      for group in groups:
      await self.channel_layer.group_add(
      'article_{0}'.format(group.article_id),
      self.channel_name
      )

      async def receive_json(self, content, **kwargs):
      command = content.get('command', None)
      article_id = content.get('article_id', None)
      if command == 'subscribe':
      await self.subscribe(article_id)
      elif command == 'unsubscribe':
      await self.unsubscribe(article_id)
      else:
      await self.send_json({
      'error': 'unknown command'
      })

      async def disconnect(self, code):
      await database_sync_to_async(self.change_user_status)(False)

      async def send_notification(self, action):
      await self.send_json(action)

      async def subscribe(self, article_id):
      await self.channel_layer.group_add(
      'article_{0}'.format(article_id),
      self.channel_name
      )

      async def unsubscribe(self, article_id):
      await self.channel_layer.group_discard(
      'article_{0}'.format(article_id),
      self.channel_name
      )

      def get_users_subscription(self):
      return CommentSubscribe.objects.filter(
      user=self.scope['user']
      )

      def change_user_status(self, online):
      return UserStatus.objects.filter(
      user=self.scope['user']
      ).update(online=online)


      views.py



      from .notify import send_comment_notification

      class CreateComment(CreateView):

      ...

      def form_valid(self, form):
      ...
      super().form_valid(form)
      send_comment_notification('create', article_id)


      class UpdateComment(UpdateView):

      ...

      def form_valid(self, form):
      ...
      super().form_valid(form)
      send_comment_notification('update', article_id)


      class DeleteComment(DeleteView):

      ...

      def delete(self, request, *args, **kwargs):
      ...
      send_comment_notification('delete', article_id)


      notify.py



      ...

      def send_comment_notification(action, article_id):
      channel_layer = get_channel_layer()
      group_name = 'article_{0}'.format(article_id)
      async_to_sync(channel_layer.group_send)(
      group_name,
      {
      'type': 'send.notification',
      'data': {
      'action': action
      }
      }
      )






      python django async-await websocket






      share|improve this question









      New contributor




      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited Dec 18 at 19:22









      200_success

      128k15150412




      128k15150412






      New contributor




      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked Dec 18 at 13:43









      KIN1991

      112




      112




      New contributor




      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      KIN1991 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          KIN1991 is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209907%2fsending-notifications-with-django-channels%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          KIN1991 is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          KIN1991 is a new contributor. Be nice, and check out our Code of Conduct.













          KIN1991 is a new contributor. Be nice, and check out our Code of Conduct.












          KIN1991 is a new contributor. Be nice, and check out our Code of Conduct.
















          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209907%2fsending-notifications-with-django-channels%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Список кардиналов, возведённых папой римским Каликстом III

          Deduzione

          Mysql.sock missing - “Can't connect to local MySQL server through socket”