Skip to content
May 8 / Nizam Sayeed

Customized comment notifications from Django

I recently had to implement a way to send notifications (using the excellent django-notification app developed by James Tauber) to users whose content is commented on in my Django web app. However, I wanted the owner/creator of the original content to get a more customized notification message. For example, if the model instance being commented on was an idea from an idea sharing app, I would like the notifications to look something like:

John Smith commented on your idea – “Switching to a better version control system”:

That’s a brilliant idea. I totally agree with you. Let’s make this happen.

Looks a lot better than just: “John Smith commented on something.”

Okay, so the first thing we need to do is to define the notice type for comments. Here are the values I used for my notice type:

  • label: comment_posted
  • display: Comment Posted
  • description: someone posted a comment for your content

from django.db.models import signals, get_app
from django.utils.translation import ugettext_noop as _
from django.core.exceptions import ImproperlyConfigured

try:
    notification = get_app( 'notification' )
        
    def create_notice_types( app, created_models, verbosity, **kwargs ):
        notification.create_notice_type( 'comment_posted', 
            _( 'Comment Posted' ), 
            _( 'someone posted a comment for your content' ) )
    
    signals.post_syncdb.connect( create_notice_types, sender = notification )    
except ImproperlyConfigured:
    print 'Skipping creation of NoticeTypes as notification app not found'

The next step is to create a signal handler and wire it to the post_save signal for the Comment model.

from django.db.models import signals
from django.contrib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_app

try:
    notification = get_app( 'notification' )
except ImproperlyConfigured:
    notification = None

# valid content types
VALID_CTYPES = [ 'link', 'note', 'update' ]

def send_comment_notification( sender, **kwargs ):
    # no point in proceeding if notification is not available
    if not notification:
        return

    if 'created' in kwargs:
        if kwargs[ 'created' ] == True:
            
            # get comment instance
            instance = kwargs[ 'instance' ]
            
            # get comment's content object and its ctype
            obj = instance.content_object
            ctype = ContentType.objects.get_for_model( obj )
            
            # check for valid content types
            if ctype.name not in VALID_CTYPES:
                return
            
            # for customized notification message
            if ctype.name == 'link':
                type = _( 'your link' )
                descr = obj.link
            elif ctype.name == 'note':
                type = _( 'your note' )
                descr = obj.title
            elif ctype.name == 'update':
                type = _( 'your status update' )
                descr = obj.update
            
            # send notification to content owner
            if notification:
                data = {
                    'comment': instance.comment, 
                    'user': instance.user,
                    'type': type,
                    'descr': descr,
                }

                # notification is sent to the original content object
                # owner/creator
                notification.send( [ obj.user ], 'comment_posted', data  )

# connect signal
signals.post_save.connect( send_comment_notification, sender = Comment )

Nothing fancy or complicated here. Some things to note:

  • I created a list of content type names called VALID_CTYPES which I use to control which content types trigger the creation of a custom notification.
  • Based on the content type’s name, I define some text that will be used for in the notification message (type).
  • I also assign a descriptive name of the item in question (descr). The descriptive name should map to whatever field in the model instance that best describes that instance. You can also use __unicode__() if you wish. You just have to make sure that it is defined in the model class and is suited for display in the notification message.
  • The notification is only sent to the owner/creator of the content being commented on.

Finally, we create a simple template for the notification e-mail and put it in templates/notification/comment_posted/:

{% load i18n %}
{% blocktrans with comment as comment and type as type and descr as descr and user.get_full_name as user %}
{{ user }} commented on {{ type }} - "{{ description }}":

{{ comment }}
{% endblocktrans %}

There you have it. Some food for thought:

  • In the code above, I only send the notification to the owner/creator of the content. However, you could easily add some code to grab a list of all the users who previously commented on the same content object and spam them as well (ala Facebook)!
  • If you have permalinks defined for your models, you could pass the actual content object to the notification template and use get_absolute_url() to display a direct link to the object’s view.
  • bartleby

    can you label which .py file you are working for the code blocks?