How to integrate reCAPTCHA with Django using a field and a widget.
From reCAPTCHA:
reCAPTCHA is a free CAPTCHA service that helps to digitize books, newspapers and old time radio shows.
A CAPTCHA is a program that can tell whether its user is a human or a computer.
You'll probably see one if you want to sign up for google apps, twitter, last.fm etc.
Although you can find a good solution on djangosnippets, I'll show you how to add reCAPTCHA in your form without writing the same code again and again.
Step 1: Apply for a reCAPTCHA key
Register on reCAPTCHA to get your public/private key pair.
Step 2: Add your keys in settings.py
RECAPTCHA_PUBLIC_KEY = '<your public key>'
RECAPTCHA_PRIVATE_KEY = '<your private key>'
Step 3: Add recaptcha-client to your project
Download recaptcha-client, create a folder (recaptcha) in your project with a copy of captcha.py inside.
Step 4: Put ReCaptchaField and ReCaptcha widget somewhere
I prefer to use a generic app (marcofucci_utils) with 2 files:
fields.py
from django.conf import settings
from django import forms
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
from marcofucci_utils.widgets import ReCaptcha
from recaptcha import captcha
class ReCaptchaField(forms.CharField):
default_error_messages = {
'captcha_invalid': _(u'Invalid captcha')
}
def __init__(self, *args, **kwargs):
self.widget = ReCaptcha
self.required = True
super(ReCaptchaField, self).__init__(*args, **kwargs)
def clean(self, values):
super(ReCaptchaField, self).clean(values[1])
recaptcha_challenge_value = smart_unicode(values[0])
recaptcha_response_value = smart_unicode(values[1])
check_captcha = captcha.submit(recaptcha_challenge_value,
recaptcha_response_value, settings.RECAPTCHA_PRIVATE_KEY, {})
if not check_captcha.is_valid:
raise forms.util.ValidationError(self.error_messages['captcha_invalid'])
return values[0]
and widgets.py
from django import forms
from django.utils.safestring import mark_safe
from django.conf import settings
from recaptcha import captcha
class ReCaptcha(forms.widgets.Widget):
recaptcha_challenge_name = 'recaptcha_challenge_field'
recaptcha_response_name = 'recaptcha_response_field'
def render(self, name, value, attrs=None):
return mark_safe(u'%s' % captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY))
def value_from_datadict(self, data, files, name):
return [data.get(self.recaptcha_challenge_name, None),
data.get(self.recaptcha_response_name, None)]
Step 5: Configure your app
I'll use django-registration because I'm lazy, but you can write your own form in the same way.
Follow these steps:
- Download django-registration
- Add
registrationto theINSTALLED_APPSsetting of your Django project - Create a custom app
registration_appwith urls.py and forms.py inside -
Add this line to <your-project>.urls.py
(r'^accounts/', include('registration_app.urls')),
registration_app.forms
from django import forms
from marcofucci_utils import fields as marcofucci_fields
from registration.forms import RegistrationForm
class RecaptchaRegistrationForm(RegistrationForm):
recaptcha = marcofucci_fields.ReCaptchaField()
registration_app.urls
from django.conf.urls.defaults import *
from registration.views import register
from registration_app.forms import RecaptchaRegistrationForm
urlpatterns = patterns('',
url(r'^register/$', register,
{'form_class': RecaptchaRegistrationForm},
name='registration.views.register'),
(r'', include('registration.urls')),
)
registration_app is now a 100% drop-in replacement for django-registration (Thanks to Ian for this).
That's it!
Note that we have used just 1 custom line of code (recaptcha = marcofucci_fields.ReCaptchaField()).
Let's take a look at http://localhost:8000/accounts/register/

In conclusion
You can either write your custom code again and again making mistakes and wasting time or write your code once and have fun!
name: your real name
email: won't be published
website: (optional) your website
comment: your comment
16 comments
Great snippet... although it didn't work for me, because of some bug in reCaptcha that I can't find... when the page loads the remote reCaptcha script, it wipes out the entire page and makes the browser never end loading something it should be loading... well, if I found out what it is, I can post the solution here, alright? But thanks nevertheless!
Never mind, Marco, the problem was that I was using it and a script was trying to transfer the contents to another DOM Element, and it was breaking because of the document.write calls... well, I'll try to solve it, tell me if you want the solution later.
Yeah, let me know what your problem was, I'm curious!
Hello Marco, great article, I'm right that is very very useful for me very soon :). Thx for share, and, one tip... you need to urgently improve font readbility of your template, is hard to read because both code and text fonts have colors to much close from background :).
see ya
Hi Felipe, thanks for your comment, I'll change the font soon, you know, sometimes it depends on monitor, calibration and stuff like that.
I'm glad that you like my snippet.
Hi, very nice snipped, certainly I'll give it a try soon. Thanks for sharing it!
Complimenti e saluti,
Pablo
Worked like a charm on one of my sites. Thanks so much for this!
Thanks. Perfect!! I appreciate the post
Very nice work. Thanks for sharing this.
Thanks so much for this! It was very easy to integrate!
Very cool. Thanks. Only thing I did different was "easy_install recaptcha_client" and then "from recaptcha.client import captcha" to use it.
Big Thanks, be happy!
very neat, thanks!
This is an amazing snippet. Well done!
My one suggestion is this modification, which makes registration_app a 100% drop-in replacement for django-registration. This is useful if you are reversing the registration.views.register view in your templates:
urlpatterns = patterns('',
url(r'^register/$', register,
{'form_class': RecaptchaRegistrationForm},
name='registration.views.register'),
(r'', include('registration.urls')),
)
@Ian I've updated the section. Thanks for the suggestion!
Nice, thank you. Would be even better with some dynamic lang setting.