Search code examples
djangodjango-viewsdjango-signals

Django signals for processing a simple user notification system


I'm learning to code and have a live Django project to keep me motivated. In my Django app, users leave comments, while others reply to the said comments.

Everytime a user refreshes their home page, I calculate whether they've received any new replies on their previously-left comments, and show a notification if they did.

This doesn't scale, because if a user has left a ton of comments and hence received a ton of replies, the calculation takes longer than for lurkers who don't create content. I want to improve the experience for these content creators.

Reading deeper into it, I've figured Django signals are the way to go. For instance, every time a new reply is left, I can fire a post_save() signal, which can be received and update the notification for the user. This way, notifications will update as replies are left - which is the way it should be.

I'll have to refactor my code, and I'm still hazy on the implementational details of the above. Can anyone provide me a quick illustrative example of how I can accomplish the above? It's going to get me started.


Currently, user reply processing is handled in the form_valid method of a CBV in my views.py class PublicreplyView(CreateView). I'm guessing I can include the following as a method in my CBV?

from django.db.models.signals import post_save post_save.connect(form_valid, sender=User)

And then elsewhere I have a different CBV that processes the notifications for every user once they refresh the home page. I guess I'll need to completely re-write that? Like I said, I'm hazy on that front.

Would love to have someone guide me what to do with an illustrative, simple example. Thanks!


p.s. I'm on Django < 1.8.


Solution

  • @Djizeus gave good outline in his comment, and I'll give you a full example. I would create separate model for notifications and create them with post_save signal on comment creation. However, I wouldn't hook this code to form valid but use post_save signal on Comment model. This way, you have cleaner separation of code. If you decide to have e.g. different forms on mobile and desktop, or if you decide to refactor PublicreplyView, you probably won't have to touch signals code.

    Django already fires post_save signals when you save Comments, so you just have to listen to them and process them. To do that, create signals.py file:

    from django.dispatch import receiver
    from django.db.models.signals import post_save
    from .models import Comment, Notification
    
    @receiver(post_save, sender=Comment)
    def auto_create_notification(sender, instance, created, **kwargs):
        if created:
            # instance holds the new comment (reply), but you also have to fetch
            # original comment and the user who created it
            parent_comment = instance.parent_comment
            parent_user = parent_comment.user            
            Notification.objects.create(user=parent_user,
                                        comment=parent_comment,
                                        type="reply_created")
    

    To hook this signal, you have to create/edit two more files. Add this to your apps.py:

    from django.apps import AppConfig
    
    # change Comment with your app name
    class CommentConfig(AppConfig):
    
        name = 'comment'
        verbose_name = 'Comment'
    
        def ready(self):
            import comment.signals
    

    Add this to your __init__.py:

    default_app_config = 'comment.apps.CommentConfig'
    

    Regarding your homepage view implementation, it depends what do you show on your homepage. If you display only notifications, then Notification ListView is a good choice. If you have a mix of different content, then I would probably user TemplateView and fetch all content you have to show in its get_context_data() method.