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
}
}
)