Comment spammers scum, Wordpress and re-CAPTCHA

The spam comments on this site were progressively getting worse and worse. It’s a ridiculous situation since all comments are only show after approval so not a single one was ever being shown on the site. But it still takes time to monitor and becomes more annoying as time goes on. This morning I had already had 10 and so decided enough is enough.

I installed the very excellent re-CAPTCHA. This captcha software is a bit different for several reasons. Firstly it has a Wordpress plugin which took 5s to install. Secondly it helps organisations digitise books, a very honourable motive. To install you just need to:

  1. Download the archive for the plugin at http://recaptcha.net/plugins/wordpress/
  2. Unzip the archive and copy the whole wp-recaptcha directory to your wp-content/plugins/ directory under Wordpress.
  3. Register at http://recaptcha.net/ , and enter your site domain to recieve a public / private key.
  4. Enable re-CAPTCHA under the Plugins section of your Wordpress admin and enter the two keys under the plugin configuration.
  5. Spend less time dealing with spam.

So how does re-CAPTCHA work then? Captchas rely on testing if you are human by quizzing you on something that should be easy for a human and relatively hard for a computer to solve. OCR, i.e. recognising the image of a word as text can be extremely hard for computers if the page is distorted, grainy or badly printed. So re-CAPTCHA asks you to OCR two words. Why two? Well one is already known since it has been confirmed by other users (how else would re-CAPTCHA know your answer was correct), the other is not known and so you are providing a useful service helping a document be recognised (and providing new captcha answers for other users). Simple but ingenius.

Django Admin – An introduction and then applying a textarea instead of a text input

Well I thought now was as good a time as any to make my first Django post. One of the best things about Django is its automatic admin interface, built very effectively from just your model objects in the ORM. What a good idea. Generally, even if a webapp isn’t there just to do CRUD, its backoffice tool almost certainly is. And even the most beautiful webapps generally have awful backoffice tools. Why? Because who the hell wants to spend their time writing a boring CRUD interface 3 people are going to see! And when does business even consider time for that sort of thing, non-customer-facing mean non-thought-about.

Here’s an example of the backoffice tool for What Ales You:

Choose beers Edit a beer


Pretty isn’t she (I presume admin interfaces have the same gender as ships?). To activate the admin interface ensure under settings.py that django.contrib.admin is included under INSTALLED_APPS and that in urls.py you have the relevant lines (they are marked with comments) included and uncommented. Then add a file called admin.py to each project where you wish to administer model objects. For example, mine for my beer project is currently:

from alephile.beer.models import Brewery, Beer, BeerRating, Media, BeerComment, UserProfile
from django.contrib import admin

admin.site.register(Brewery)
admin.site.register(Beer)
admin.site.register(BeerRating)
admin.site.register(BeerComment)
admin.site.register(Media)
admin.site.register(UserProfile)

This tells Django which model objects I want to be able to edit. You’ll notice that my beer description is a normal text input which is a bit annoying, what I’d prefer is a textarea. To achieve this I need to add a ModelAdmin and a ModelForm. The ModelAdmin is a class telling Django how to administer a particular model object. Each ModelAdmin contains a ModelForm which tells it how to create the editing form. By default this just includes all editable fields, but now we are going to override the ModelAdmin and tell it to use a custom form instead. We end up with:

from alephile.beer.models import Brewery, Beer, BeerRating, Media, BeerComment, UserProfile
from django.contrib import admin
from django import forms

class BeerModelForm( forms.ModelForm ):
    description = forms.CharField( widget=forms.Textarea )
    class Meta:
        model = Beer

class BeerAdmin( admin.ModelAdmin ):
    form = BeerModelForm

admin.site.register(Brewery)
admin.site.register(Beer, BeerAdmin)
admin.site.register(BeerRating)
admin.site.register(BeerComment)
admin.site.register(Media)
admin.site.register(UserProfile)

and an interface that looks like so. Easy when you know how but something I initially struggled to find out.

A better description field