In the last few weeks I’ve been getting an increasingly amount of comment spam. This was expected though — I’ve done nothing to actually keep spam out. For a tiny blog like mine I feel the two traditional ways of blocking spam; user registration and CAPTCHAs, are not very user friendly.
As a first line of defense I decided to disable commenting for posts older than 2 weeks. As always with Django, it turned out to be much simpler than I thought.
Step 1: Create a template filter
The template filter is very straight forward. It takes a date and returns True/False if the date is within 14 days. I hardcoded the limit to 14 days in my example, however it should probably be an setting in settings.py. If you do not know or remember how to create a template filter, Django has some excellent documentation on the subject.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from django import template import datetime register = template.Library() @register.filter def showcomments( date ): date_adjusted = date + datetime.timedelta( days=14 ) if datetime.datetime.now() <= date_adjusted: return True return False |
Step 2: Modify the blog template
In your blog post template add an if statement to only show the comment form if our showcomments filter returns true.
1 2 3 4 5 |
{% if object.published|showcomments %} {# show comments form #} {% else %} {# show comments has been disabled message #} {% endif %} |
Step 3: Stopping spam at the view level (added)
As some mentioned in the comments — chances are the spammers will figure out how to post comments even if you hide the form. To block comments at the a lower level — the view level — here’s a quick and crude way of wrapping around the free comments post_free_comment() method.
Override /commments/postfree/ in your urls.py:
1 |
( r'^comments/postfree/$', 'mysite.blog.views.post_comment' ), |
Create the post_comment in your views.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import datetime from django.shortcuts import get_object_or_404 from django.http import HttpResponseForbidden from django.contrib.comments.views.comments import post_free_comment from mysite.blog.models import Post def post_comment( request ): comment_id, post_id = request.POST['target'].split( ':' ) post = get_object_or_404( Post, pk=post_id ) date_adjusted = post.published + datetime.timedelta( days=14 ) if datetime.datetime.now() <= date_adjusted: return post_free_comment( request ) return HttpResponseForbidden( "Error 403: Spam forbidden" ) |
If you have small tips on how to fight spam — that’s user friendly — please share it in the comments.
#1 peschler
Posted: May 24, 2008
Comment spam on my blog http://pyjax.net started about a month ago. All comments contain links to a Google group :(
I am using the django-comment-utils marking every first-time poster non-public. A blog entry only shows public comments, so spam is not visible on the page. It is visible in the admin and I added a simple view which deletes all non-public comments of a blog entry. Works like a charm.
I also made the “comment disable” functionality part of the blog entry model by adding a field “allow_comments”. That way I can control it on a per entry basis via the admin.
#2 Peter Sanchez
Posted: May 24, 2008
I have something similar for my blog but I actually wrote the function as a method of the Entry model. In the template I just use…
{% if entry.can_post_comments %}
I also use Akismet spam filter, which catches a TON of spam. Some how my little blog became a favorite of spammers ;)
#3 James Bennett
Posted: May 24, 2008
Simply hiding the comment form won’t work, though, because automated spam bots will figure out the URL to post to during the period when it’s visible, store that URL and continue posting to it in perpetuity. The only genuine solution is to actually apply server-side moderation rules to the incoming comments, I’ve written a little app which makes that easy:
http://code.google.com/p/django-comment-utils/
#4 Magic
Posted: May 24, 2008
A nice tip about preventing spam using Django: http://fi.am/entry/preventing-spam/
#5 Andrew J
Posted: May 24, 2008
Just disabling the comment form isn’t necessarily going to prevent comment spam from being added to older comments, you usually need to stop it in the model or the view as the previous commenters have described. Spammers look at the form submission information and use their own code to actually submit the spam, so you have to actively stop comments from being accepted on those older blog entries as well as not display the comment form.
#6 Julian
Posted: May 24, 2008
Use Askimet and no more worrying - nada!
#7 jinzo
Posted: May 25, 2008
I’m using akismet too. Works almost flawlessly.
#8 Paul Bx
Posted: May 25, 2008
Akismet has been blocking about 150 - 200 spam attempts per day on my blog, making it worthwhile despite my concerns about a centralized, proprietary, single point of failure. I’m planning to add DNSBL checks into the mix as well.
#9 Zeth
Posted: May 25, 2008
I recently changed my blog to be Django powered. (Hopefully the markdown works).
Preventing my spam is my problem, it is not fair to make it my readers’ problem, so I have avoided guess the picture.
My spam measures so far are to:
#10 Zeth (supplemental)
Posted: May 25, 2008
I have not yet used akismet on my blog.
However, I have added IP blocking. I set it up as follows. When I delete a comment, on the “are you sure you want to delete this comment” page, there is a “Block IP address?” checkbox (checked by default). When the comment is deleted, the IP Address is added to a block list. Blocked IP Addresses are blocked sitewide, saving me a little bandwidth.
#11 Beetle B.
Posted: May 29, 2008
Another vote for Akismet!
#12 sorl
Posted: May 30, 2008
I use javascript to display the whole form (no js = no post) using this: http://www.djangosnippets.org/snippets/642/. This method has worked very good = no spam at all so far…
I’ve also developed some other stuff using a hidden key making sure the form takes more than 10 seconds to fill in and less than 24 hours… it hasn’t been as successful as the js method however…