Django 1.0 Comments

by · Posted in: website business

Over the weekend I cobbled together a comment system for this journal. You should see links near the headlines and a comment form on the main entry page. Hopefully it is self-explanatory. I've allowed what I think is a reasonable amount of html in the comments, but the more distracting or potentially destructive tags will be filtered out. The preview will help figure out what works and what doesn't.

The comment system is provided by the Django framework that I use for the whole site. Django makes it quite easy to get a working system up quickly, but because the comment system was reworked in Django 1.0 and not everything is fully documented yet, it can be a chore to figure out how to customize everything. Here are a few notes in case anyone arrives here trying to solve the same problems I did.

The Templates

The easiest way to change the look of everything is to add a 'comments' directory to you template directory and populate it with template files to override the defaults. For basic comments you will probably be most interested in:

  • comments/form.html
  • comments/preview.html
  • comments/posted.html

It should be pretty obvious what these do. Sending users to a 'Thanks for Posting' page, which is the default behavior, seems like a waste of time for everyone involved. Instead I wanted them return to the original page with their newly crafted comment in all its glory and completely bypass the posted.html template. How to accomplish is less than obvious and the documentation is presently mum on the issue. It turns out you can supply a 'next' variable to the comment system with a url pointing where to go after the comment is posted. I found the easiest way to do this is to add a hidden field in the form like this:

<input type="hidden" name="next" value="{{note.get_absolute_url}}" id="next" />

where 'note' is the blog entry object. Django will redirect the post to the given url with a query string appended to it containing the comment ID. This was somewhat unexpected, but turns out to be handy if you want to do something like scroll down the page to the comment, which I've done here with a splash of javascript.

Email Notification

Since I'm not sending comments through a moderation system before posting them live, I want to be notified of new comments. The Django signal system makes this exceptionally easy by sending several useful signals by default. To catch the signal you simply need to write a function and register it. It doesn't matter where the function lives so long as the code gets run when Django starts up. I put it in the model.py for the journal application. Although it bugs because it seems like bad MVC practice, this an easy place to find it and it is guaranteed to execute before anything else gets cooking.

from django.contrib.comments.signals import comment_will_be_posted
from notes.templatetags.html_filters import cleanHTML
def comment_notification(sender, **kwargs):
     comment = kwargs['comment']
     comment.comment = cleanHTML(comment.comment)
     subject = 'New Comment on %s' % comment.content_object.headline
     msg = 'Sender: %s (%s)\n %s\n\nComment:\n%s' % 
         (comment.user_name, comment.user_email,comment.user_url, comment.comment)
     send_mail(subject, msg, 'to@example.com', ['from@example.com']) 
comment_will_be_posted.connect(comment_notification)

(Note: in most cases to send email you will need to make sure you have supplied server/authentication information in the settings.py file.)
The last line registers this function with the comment_will_be_poster signal so it will be called right before a comment is saved to the database. The reason I call it before the comment is saved is so I can sanitize the comment of any potentially mischievous html which you can see in the above code as the call to cleanHTML(). See below for more.

Preventing disruptive HTML in comments

By default Django will escape all html, but I would like to allow some flexibility for users to be a bit more expressive. To do this without allowing malicious things like script and style tags I used this advice from Tom Insam. This code requires the BeautifulSoup script be somewhere in your python path. I used Tom's ideas to create a template tag that sanitizes user-provided html making it easy to use within a template such preview.html template like this:
{{ comment|cleanHTML|safe|linebreaksbr }}
I can also call it as a normal function as in the code above right before it is saved in the database. This allows the same function to perform double duty and insures that the preview the user sees will be the same as the posted comment.

Like everything else around here, it is a work in progress. If you have any ideas or suggestions...well we now have comments.