Sending notifications with Django channels
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
New contributor
add a comment |
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
New contributor
add a comment |
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
New contributor
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
python django async-await websocket
New contributor
New contributor
edited Dec 18 at 19:22
200_success
128k15150412
128k15150412
New contributor
asked Dec 18 at 13:43
KIN1991
112
112
New contributor
New contributor
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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.
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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