Login

ReCaptcha for django forms (improved and with remoteip)

Author:
pinkeen
Posted:
December 16, 2010
Language:
Python
Version:
1.2
Score:
0 (after 0 ratings)

My previous snippet with captcha wasn't very portable but Marco Fucci figured out the thing that I couldn't - value_from_datadict function. So all credits go to him and his snippet, I adapted it to my needs, maybe you like my version better - it doesn't need any captcha libraries and let's you modify the widget's html easily. Also I added an option to pass remoteip to google api's verify method.

How to use it:

In your settings.py add:

RECAPTCHA_PUBKEY = 'your recaptcha public key' RECAPTCHA_PRIVKEY = 'your recaptcha private key'

After that just import and use ReCaptchaField in your form as you would any other field. That's it.

Important If you want to have peace of mind in case google decided that the remoteip parametr is mandatory then: Derive every form that has the captcha field from ReCaptchaForm and when you create the form object after receiving POST/GET, pass a remoteip parameter like that:

form = YourCaptchaForm(data=request.POST, remoteip=request.META['REMOTE_ADDR'])

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
'''
ReCaptcha for django forms

All credits go to Marco Fucci, I used his idea here and adapted it to my needs.
Contact: Filip Sobalski <[email protected]>

How to use it:

In your settings.py add:

RECAPTCHA_PUBKEY = 'your recaptcha public key'
RECAPTCHA_PRIVKEY = 'your recaptcha private key'

After that just import and use ReCaptchaField in your form as you would any other field. That's it.

*** Important *** If you want to have peace of mind in case google decided that the remoteip parametr is mandatory then:
Derive every form that has the captcha field from ReCaptchaForm and when you create the form object after receiving POST/GET, pass a remoteip parameter like that:

form = YourCaptchaForm(data=request.POST, remoteip=request.META['REMOTE_ADDR'])
'''

from django import forms
from django.utils.safestring import mark_safe
from django.utils.encoding import smart_unicode

import settings

import urllib
import urllib2


class ReCaptchaWidget(forms.Widget):

	def get_recaptcha_widget_html(self, field_name):
		widget = u''
		widget += u'<div class="recaptcha_box" id="fdiv_%s"></div>' % field_name
		widget += u'<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>'
		widget += u'<script type="text/javascript">'
		widget += u'Recaptcha.create("%s", "fdiv_%s", {theme: "white"});' % (settings.RECAPTCHA_PUBKEY, field_name)
		widget += u'</script>'
		
		return widget
		
        def value_from_datadict(self, data, files, name):
		
		remoteip = None
		
		if 'REMOTE_ADDR' in data:
			remoteip = data.get('REMOTE_ADDR')
		
		return {
			'challenge' : data.get('recaptcha_challenge_field', None), 
			'response' : data.get('recaptcha_response_field', None),
			'remoteip' : remoteip
		}
	
	def render(self, name, value, attrs=None):
		return mark_safe(self.get_recaptcha_widget_html(name))
		
class ReCaptchaField(forms.Field):

	widget = ReCaptchaWidget
	required = True
	
	def verify_captcha(self, challenge, response, remoteip):
		url = 'http://www.google.com/recaptcha/api/verify'
		values = {
			'privatekey': settings.RECAPTCHA_PRIVKEY, 
			'challenge' : challenge, 
			'response' : response
		}
		
		if remoteip:
			values['remoteip'] = remoteip
			
		vrequest = urllib2.Request(url, urllib.urlencode(values))
		vresponse = urllib2.urlopen(vrequest)
		result = vresponse.read().split('\n')
		
		if result[0] == 'true':
			return (True, result[1])
			
		return (False, result[1])
		
	def clean(self, value):
		super(ReCaptchaField, self).clean(value['response'])
		
		result = self.verify_captcha(value['challenge'], value['response'], value['remoteip'])
		
		if not result[0]:
			raise forms.ValidationError('Wrong answer, try again.')
		
		return value['response']

		
class ReCaptchaForm(forms.Form):
	
	def __init__(self, data=None, remoteip=None, *args, **kwargs):
		if data and remoteip:
			data = data.copy()
			data['REMOTE_ADDR'] = remoteip
			
		super(ReCaptchaForm, self).__init__(data=data, *args, **kwargs)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

iamwithprograms (on February 16, 2011):

Hey pinkeen, nice snippet.

Got a question for you though. Have you had any problems with the "recaptcha_challenge_field" and "recaptcha_response_field" inputs not existing in your POST data? It seems that using the javascript api prevents these fields from being added to my post data. When I disable javascript everything works fine... any ideas?

I know you have removed the captcha dependency but... it might be better to use it since captcha's displayhtml() method for rendering provides a noscript version of the field.

django 1.2.5 python 2.7 ^^

#

iamwithprograms (on February 16, 2011):

Nevermind. It turns out the template I was using surrounded the form with a table which caused the fields to not be posted. Moving the form elements to surround the table resolved this issue. ^^

#

Please login first before commenting.