Django Comments and Signals

So much for posting regularly. About the only time I get around to posting is when I’m actually developing for the blog, which is what I’m doing at the moment. After speaking with a friend about the Django comments framework, I thought I’d share a few snippets of code I’ve used here.

First up are the functions I’ve registered against the signals framework:

from django.contrib.comments.signals import comment_will_be_posted, comment_was_posted
from django.core.mail import mail_managers
from django.contrib.sites.models import Site

def mark_comment_from_unauthenticated_user(sender, **kwargs):
    """
    If a comment being posted is arriving from a member of the public, rather
    than an authenticated user, we set is_public to false in order to prevent
    tonnes of spam being displayed the blog.
    """
    comment = kwargs.get('comment', None)
    if comment is None:
        return True
    if comment.user is None:
        comment.is_public = False
    return True

def notify_managers(sender, **kwargs):
    """
    Notifies all settings.MANAGERS that a fresh comment has been posted.
    """
    comment = kwargs.get('comment', None)
    if comment is None:
        return True
    public = "public"
    if not comment.is_public:
        public = "flagged for approval before being made public.  Please visit http://frio.name/admin/comments/comment/%s/ to approve the comment" \
            % comment.id
    email = """A new comment has been posted to %s.  The comment is %s.\n\n---\n%s""" \
        % (Site.objects.get_current().name, public, comment.get_as_text())
    mail_managers(
        "A new comment has been posted!",
        email,
        fail_silently=True
    )
    return True

comment_will_be_posted.connect(mark_comment_from_unauthenticated_user)
comment_was_posted.connect(notify_managers)

Cumulatively, these two functions first mark anonymous comments as private (and therefore prevent them from being displayed), and mail me whenever a comment was actually posted. This brings the comments framework inline with more traditional blog software.

Unfortunately, since deploying that I’m being forced to revamp those functions. I get something like 10 spam comments a day, despite the fact I gather fuck all traffic. Cleaning those comments out in Django’s admin is tiresome at best, and the problem is only going to get worse. I’ll make another post shortly with the revised mark_comment_from_unauthenticated_user() (update - posted here).

Other things that are worth noting when working with comments:

  • The templates you’ll need are comments/preview.html and comments/posted.html, along with a comments/base.html (but this can simply contain a \{\% extends 'base.html \%\} for your site base).
  • In comments/preview.html, you’ll need to \{\% load comments \%\}. Once you’ve done that, the form’s action should head to the comment_form_target variable Django exposes. The unsaved comment is also exposed in the comment variable.
  • In comments/posted.html, Django exposes the new comment in the comment variable. Obvious, I’m sure, but it’s pretty badly documented and it took me a while to figure out that it had changed from the earlier comments framework. You can therefore link to the comment’s originating object (with the comment displayed) by using comment.get_absolute_url in your template.
  • Django’s honeypot sucks as an anti-spam solution.

UPDATE - it’s worth noting that since switching to Jekyll as a generator, I no longer use Django’s comments utility so you won’t be able to see the above code in action anymore. I’ll endeavour to clean up my original blog’s code a bit and publish it on github.

Posted: 08 Jan, 2009
Tags:
blog comments powered by Disqus