Hacking comments without hacking comments

Way back when I first set up this site, and threw together a minimal blog application to power it, I didn’t bother much with enhancing Django’s comment system. But later on, when I started getting a lot of comment spam (trivia: as of this moment, 15,153 comments have been posted here, and 14,406 of them — roughly 95% — have been spam) I realized I needed to do something, and quickly hacked Django’s comments application to add moderation.

At the time it seemed a reasonable thing to do, but it’s been haunting me ever since; having to port a bunch of hacks whenever I want to upgrade Django is a pain. A while back I posted the beginnings of an unobtrusive moderation system using signals, but it wasn’t everything I wanted it to be, and as I worked on rewriting my blog application I found myself writing the same or similar code in multiple models in order to work with it. Any time I’m in that situation, I stop, step back and break out that code into its own module or application, and usually do my best to make it more generic and reusable.

The result, this time around, is comment_utils, an open-source library of useful tools that build on Django’s bundled comments application.

The big feature for this app is a generic, unobtrusive, extensible system for comment moderation which includes the ability to turn on any or all of the following options, on a per-model basis:

  • Akismet spam filtering
  • Auto-closing comments a certain number of days after an object’s publication
  • Auto-moderating comments a certain number of days after an object’s publication
  • Auto-moderating all comments
  • Auto-moderating a new commenter’s first posts until one of them is approved
  • Automatically enabling/disabling comments for an object via a BooleanField on the model
  • Automatically emailing you whenever a comment is posted on an object

All of this is handled with as little work as possible; Akismet filtering, for example, can be turned on with two lines of code. Also included is the ability to add your own custom moderation methods whenever and wherever you need them. See the documentation for all the gory details, and examples of how the system works.

Also bundled with comment_utils:

  • The moderation-oriented template tags which began life as part of template_utils.
  • A custom manager which can figure out the “most-commented” objects of any model it’s used on.

I’ve done some basic testing and verified that everything at least works on my local setup with an application I’ve been working on but, this being the first time the code’s seen the public light of day, bug reports would be appreciated.

Comments

Eric Florenzano
June 25, 2007
#

This freaking rocks. After work today I’m going to go through it and try and integrate it with my site.

The funny thing is that I was about to implement Akismet spam filtering on my comments, and you’ve now done the work for me, thanks!

Baxter
June 25, 2007
#

Great Stuff, James. I’ll be testing it soon.

nate
June 25, 2007
#

holy smokes you’re a savior, at all the right moments. i swear each time i go to implement something on a django site, i find you’ve got a much better answer than i could ever come up with. here you’ve done it again!

i’ve seen comment spam trickling in on all my django installs and i’ve been dreading diving into hacking akismet (a savior in its own right!) into the mix.

thx so much for putting this together.

peter
June 25, 2007
#

You’re my hero. This is exactly what i need.

Thanks for the tutorial and a great site.

Jeff Wheeler
June 25, 2007
#

I think my site just might become 100% your code soon. :D

Thanks!

James Bennett
June 26, 2007
#

I just fixed a little bug in the Akismet filtering; you might want to download a fresh copy or SVN up if you did a checkout.

David, biologeek
June 26, 2007
#

I’m just curious, I’ve read somewhere (maybe here) that the contrib.comments package need to be rewrite in order to allow more flexibility (like user.get_profile) and to use newforms. I thought that’s the main reason why this part is not documented.

What’s the status of this update?

Anyway, thanks for this new app :-).

ps : how did you handle the site URL? Have you hacking your django comments or did you use the name charfield like I probably do eventually?

James Bennett
June 26, 2007
#

I hacked name and URL fields into the comments app. When I switch servers (soon) I’ll be moving to a new blog codebase running entirely on stock Django, and I’ll be getting rid of that.

Bryan
June 26, 2007
#

I’m in the same boat as Jeff there. First registration, then the contact form now this? Did I say I loved you? Because… I’m ready to profess it. :P

You’re making my work SO much easier.

Cam McVey
June 27, 2007
#

Hi James,

Just wanted to thank you for another ROCKIN’ package of goodies.

A framework is only really as good as its community, which is why Django is SO good.

Cheers, Cam

Ludvig Ericson
July 9, 2007
#

BUY VIAGRA http://imaginarysite.com/ http://example.org/ FREE VIAGRA

On a serious note, I’d want to see a collection of your django apps that are spread throughout the time-space continuum.

Michael
July 12, 2007
#

First, thanks so much for this. Your code and contributions are just amazing.

Second, I have what is probably a really stupid question that is only somewhat related.

Is it possible to use freecomment and comment side by side? I haven’t (knowingly) seen an example of this or read docs on it, but it seems like it would be nice to have the ability to rate posts and whatnot - if you were logged in - but still post, shall we say, lesser comments if you weren’t. Am I missing something totally, or…?

Stanislaus Madueke
July 24, 2007
#

Hi James,

In the sidebar where you display stuff about this post, it says “68 comments” - I can only count about 12. I’m thinking maybe the difference is the un-moderated comments with “is_public = False”. Was that intentional, or did you forget to use the {% get_public_xx_count %} tags from comment utils?

Speaking of comment utils, I’m stuck with some error, trying to use it in an app (using the 0.96 release of django). I recall reading somewhere that most (all?) of your apps are written to work with the SVN release of django…can’t seem to find that link now :( If that’s the case, I won’t trouble you any further - I know I could add the functionality I need myself; it’s just…it would’ve been so freaking cool if comment_utils just worked…

Oh, I almost forgot. Here’s the error::

Request Method: GET 
Request URL: http://localhost:8000/admin/ 
Exception Type: AlreadyModerated 
Exception Value: The model ‘post’ is already being moderated 
Exception Location: ..\comment_utils\moderation.py in register, line 376

Here’s my CommentModerator subclass::

class PostModerator(CommentModerator):
    ”’Defines comment moderation options for blog posts”’
    auto_close_field = ‘created_date’
    close_after = 30
    enable_field = ‘enable_comments’
    email_notification = True

    def email(self, comment, content_object):
        ”’
        Override base method so we send mail to post author _rather_ than site admins.
        Only send mail if author elected to receive it (i.e. “receive_comment_notification“ is True).
        ”’
        if self.email_notification and content_object.receive_comment_notification:
            recipients = [content_object.author.email]
            t = get_template(‘comments/comment_notification.txt’)
            c = Context({
                ‘comment’: comment,
                ‘content_object’: content_object
            })
            subject = ‘New comment posted on “%s”’ % content_object
            message = t.render(c)
            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipients, fail_silently=True)

And the registration line::

moderator.register(Post, PostModerator)

The model being moderated (Post) belongs to a blog application. That’s pretty much it…nothing fancy.

Thanks for a great site btw - you’ve certainly been mostly responsible for turning me from django newbie to power user, in such a short time - rock on!

Add a comment

You may use Markdown syntax in your comment, but raw HTML will be removed. By posting a comment here, you are agreeing to the terms of my comment policy.