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.htmlandcomments/posted.html, along with acomments/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 thecomment_form_targetvariable Django exposes. The unsaved comment is also exposed in thecommentvariable. - In
comments/posted.html, Django exposes the new comment in thecommentvariable. 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 usingcomment.get_absolute_urlin 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.
Tags: