Chris Umbel

Using reCAPTCHA With Django

django I sure was naive. When I launched a certain django-based site that accepted user comments (wonder which one that is?) a while back I thought I could block the comment spam myself without CAPTCHA. After a few months of traffic I started getting hammered with it and tried blocking IPs, keywords and patterns. All to no avail.

The trouble-spot was a strait-forward, regular old HTML form that accepted the comment input. I needed it to appeal to wide browser requirements of the site. My AJAX-jQuery-to-django-piston-service comment submissions rarely were the source of spam entry but I needed my regular forms locked down as well.

reCAPTHCA logo

I toyed with the idea of rolling my own CAPTCHA but I honestly have bigger fish to fry. Turns out that integrating reCAPTCHA with django was a sinch and solved my comment spam problems.

Here's how to do it.

Step #1: Get a reCAPTCHA Account

reCAPTCHA is a service and all the heavy lifting is done on reCAPTCHA's servers. Because of that you must sign up for an account to use the service here.

By default a key works on a single domain, but you can also create your key as "Global" allowing them to work on multiple site.

Step #2: Install recaptcha-client

In order for django (or any other python code) to use the reCAPTCHA service you must install the recaptcha-client library. This is most easily accomplished with setuptools:

easy_install recaptcha-client

Alternatively you can install it directly from source by downloading it from: http://pypi.python.org/pypi/recaptcha-client

Step #3: Add reCAPTCHA to a Template

Now it's time for some actual web development. I'll start out by putting the familiar reCAPTCHA interface in a django template. Notice that I have it as part of a form named edit_form. There are also a couple locations where you have to insert your public key which you get when you sign up for a reCAPTCHA account.

<form  action="#" method="POST">
  <table>
    {{ edit_form }}
    <tr>
        <th>Are you human?</th>
        <td>
            <span class="validation_error">{{ captcha_response }}</span>
        
            <script type="text/javascript"
            src="http://api.recaptcha.net/challenge?k=[[ YOUR PUBLIC KEY ]]">
            </script>
            
            <noscript>
            <iframe src="http://api.recaptcha.net/noscript?k=[[ YOUR PUBLIC KEY ]]"
            height="300" width="500" frameborder="0"></iframe><br>
            <textarea name="recaptcha_challenge_field" rows="3" cols="40">
            </textarea>
            <input type="hidden" name="recaptcha_response_field" 
            value="manual_challenge">
            </noscript>
        <td>
    </tr>
    <tr>
        <th></th>
        <td><input type="submit" value="Save"/></td>
    </tr>
  </table>
</form>

Step #4: Handle reCAPTCHA Upon Form Submission

Now I'll set up a view to handle the template and form submission. This would all live in your application's views.py.

# load the recaptcha  module
from recaptcha.client import captcha

# create the form to be submitted
class EditForm(forms.Form):
    data_field = forms.CharField()

def myview(request):	
    if request.method == 'POST':
        edit_form = EditForm(request.POST)
        # talk to the reCAPTCHA service
        response = captcha.submit(
            request.POST.get('recaptcha_challenge_field'),
            request.POST.get('recaptcha_response_field'),
            '[[ MY PRIVATE KEY ]]',
            request.META['REMOTE_ADDR'],)
        
        # see if the user correctly entered CAPTCHA information
        # and handle it accordingly.
        if response.is_valid:
            captcha_response = "YOU ARE HUMAN: %(data)s" % {'data' :
		edit_form.data['data_field']}
        else:
            captcha_response = 'YOU MUST BE A ROBOT'
        
        return render_to_response('mytemplate.html', {
                'edit_form': edit_form,
                'captcha_response': captcha_response})
    else:
        edit_form = EditForm()
        return render_to_response('mytemplate.html', {'edit_form': edit_form})

Which would look like:

If a user enters the CAPTCHA text correctly they get a message indicating that they are human. This is where you would put logic to do something like save a comment. If a user fails to enter the CAPTCHA text correctly they get a nasty error message telling them that they must be a robot. In that code path you'd want to assume the user either entered the text wrong and will retry or that there is no real user at all.

Conclusion

It's unfortunate that we have to deal with spam bots and other abuses of our hard work. Luckily services like reCAPTCHA make it relatively easy to defend against. And the benefits extend beyond just protecting our own web content. Every time a user uses reCAPTCHA they're actually helping to digitize books on the other end.

Sat Nov 21 2009 15:11:00 GMT+0000 (UTC)

Follow Chris
RSS Feed
Twitter
Facebook
CodePlex
github
LinkedIn
Google