Django + reCAPTCHA
Portland Things has a submission form for recommending a new blog for inclusion on the site. It wasn’t up for long before spammers started submitting garbage to it. Eventually I got sick of deleting the garbage and implemented a captcha, using reCAPTCHA — arguably the best captcha implementation around.
As the page it’s used on is a very simple form, I figured it’d serve as a good example of how to add reCAPTCHA to a Django form.
First off, sign up with reCAPTCHA and get your key. settings.py seems like an appropriate place for it, so add something along the lines of this:
RECAPTCHA_PUBLIC_KEY = '<your public key>'
RECAPTCHA_PRIVATE_KEY = '<your private key>'
Then install the Python client library:
easy_install recaptcha-client
or
pip install recaptcha-client
We then just need to add a little more conditional logic to our view function, checking not only whether the form was valid before saving it, but also now checking with reCAPTCHA to see if the captcha was correct.
My view code went from this:
if request.method == 'POST':
form = ProposedFeedForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/localblogs/submit/success/')
else:
form = ProposedFeedForm()
return render_to_response('addablog.html', { 'form': form })
to this:
from recaptcha.client import captcha
captcha_error = None
if request.method == 'POST':
form = ProposedFeedForm(request.POST)
if form.is_valid():
captcha_response = captcha.submit(
request.POST.get('recaptcha_challenge_field', ''),
request.POST.get('recaptcha_response_field', ''),
settings.RECAPTCHA_PRIVATE_KEY,
request.META['REMOTE_ADDR'],
)
if captcha_response.is_valid:
form.save()
return HttpResponseRedirect('/localblogs/submit/success/')
else:
captcha_error = captcha_response.error_code
else:
form = ProposedFeedForm()
return render_to_response('addablog.html', {
'form': form,
'captcha_html': captcha.displayhtml(
settings.RECAPTCHA_PUBLIC_KEY, error=captcha_error),
})
displayhtml() takes care of all the HTML for you. You just pass it into your template, and then show it to the user:
<p>{{ captcha_html|safe }}</p>
And that’s it!
Since implementing this, I’ve had zero spam.
Note on copyright: I believe the code in this post is trivial enough to be ineligible for copyright. In case that is not true, I hereby release it under the CC0 license.