Login

Unobtrusive comment moderation

Author:
ubernostrum
Posted:
March 17, 2007
Language:
Python
Version:
Pre .96
Score:
19 (after 19 ratings)

Before using this snippet, please note that it's largely been superseded by comment_utils, which includes a more featureful and extensible version of this system, particularly with respect to additional moderation options and useful things like email notifications of comments.

Once upon a time I hacked the copy of django.contrib.comments I'm using on my blog, so that I could have comments get set to is_public=False if posted more than 30 days after the entry's publication, and to add Akismet spam filtering. I've regretted it ever since, because it's made upgrading my copy of Django a pain.

So here's an improved version which doesn't require hacking directly on Django. To use it, you'll need to do a few things:

  1. Grab the Python Akismet module and install it somewhere on your server.
  2. In your settings file, add AKISMET_API_KEY, and make sure its value is a valid Akismet key. If you don't have an Akismet key, you can get one at wordpress.com.
  3. Put this code -- both the function and the dispatcher calls -- somewhere in your project that's guaranteed to be imported early (until this code is executed, the moderation function won't be set up to listen for comments posting).

To have comments on a certain type of object (say, weblog entries) automatically go into moderation when the object reaches a certain age, define a method on that object's model called comments_open, and have it return False when comments should be auto-moderated.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from django.conf import settings
from django.db.models import signals
from django.dispatch import dispatcher
from django.contrib.comments.models import Comment, FreeComment
from django.contrib.sites.models import Site

def moderate_comments(sender, instance):
    """
    Applies comment moderation to newly-posted comments.
    
    Moderation happens in two phases:
    
        1. If the object the comment is being posted on has a method
           named ``comments_open``, it will be called; if the return
           value evaluates to ``False``, the comment's ``is_public``
           field will be set to ``False`` and no further processing
           will be done.
    
        2. If the object did not have a ``comments_open`` method, or
           if that method's return value evaluated to ``True``, then
           the comment will be submitted to Akismet for a spam check,
           and if Akismet thinks the comment is spam, then its
           ``is_public`` field will be set to ``False``.
    
    """
    if not instance.id: # Only check when the comment is first saved.
        content_object = instance.get_content_object()
        comments_open = getattr(content_object, 'comments_open', None)
        if callable(comments_open) and not comments_open():
            instance.is_public = False
        elif hasattr(settings, 'AKISMET_API_KEY') and settings.AKISMET_API_KEY:
            from akismet import Akismet
            akismet_api = Akismet(key=settings.AKISMET_API_KEY,
                                  blog_url='http://%s/' % Site.objects.get_current().domain)
            if akismet_api.verify_key():
                akismet_data = { 'comment_type': 'comment',
                                 'referrer': '',
                                 'user_ip': instance.ip_address,
                                 'user_agent': '' }
                if akismet_api.comment_check(instance.comment, data=akismet_data, build_data=True):
                    instance.is_public = False

dispatcher.connect(moderate_comments, sender=Comment, signal=signals.pre_save)
dispatcher.connect(moderate_comments, sender=FreeComment, signal=signals.pre_save)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 9 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 4 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 4 months ago
  5. Help text hyperlinks by sa2812 1 year, 5 months ago

Comments

jeffwheeler (on March 17, 2007):

This is wonderful. I just learned about Django’s use of Signals today, and this is one of the first things to come to mind (after threading with the API calls I made earlier, of course).

I really like this function; it definitely seems like the cleanest way to do this.

I think it's also very similar to how the Askimet plugin for WordPress works.

#

rajeshd (on April 18, 2007):

James,

Just wanted to say that this is very useful. I love innovative applications of Django's signals!

-Rajesh

#

jheasly (on June 13, 2007):

When you say

Put this code -- both the function and the dispatcher calls -- somewhere in your project that's guaranteed to be imported early

do you mean like somewhere (up high) in views.py?

#

ubernostrum (on June 18, 2007):

The best place is in a models file or the __init__.py of an application.

#

Please login first before commenting.