Login

Semi-Portable recaptcha integration with django forms

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

It is not so portable and easy as I wanted it to be because of how django forms work - they don't play well with recaptcha.

To get it to work:

  • Add two variables to your app settings, RECAPTCHA_PUBKEY and RECAPTCHA_PRIVKEY
  • Derive forms you want to have a captcha from the provided ReCaptchaForm class (how to get it working with ModelForm? any ideas?)
    • If you override the form's clean method make sure you firstly call the ReCaptchaForm's clean method *
  • In your view, upon receiving the form data initialize the objects like this form = YouFormClassDerivedFromReCaptchaForm(remoteip=request.META['REMOTE_ADDR'], data=request.POST) (or request.GET of course) - this is because reCaptcha needs the user's remote ip.
  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
104
105
'''
Semi-Portable recaptcha integration with django forms
Contact: Filip Sobalski <[email protected]>

- Add two variables to your app settings, **RECAPTCHA_PUBKEY** and **RECAPTCHA_PRIVKEY**
- Derive forms you want to have a captcha from the provided ReCaptchaForm class (how to get it working with ModelForm? any ideas?)
- * If you override the form's clean method make sure you firstly call the ReCaptchaForm's clean method *
- In your view, upon receiving the form data initialize the form object like this:
form = YouCaptchaFormClass(remoteip=request.META['REMOTE_ADDR'], data=request.POST) (or request.GET of course) - this is because reCaptcha needs the user's remote ip.
'''

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

import settings


import urllib
import urllib2


class DummyWidget(forms.Widget):
	is_hidden = True

	def render(self, name, value, attrs=None):
		return ''
		
class DummyField(forms.Field):
	
	widget = DummyWidget
	
	def clean(self, value):
		return value


class ReCaptchaWidget(forms.TextInput):
	
	def get_recaptcha_widget_html(self, field_name):
                ''' Template for this probably would be an overkill '''
		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", callback: Recaptcha.focus_response_field});' % (settings.RECAPTCHA_PUBKEY, field_name)
		widget += u'</script>'
		
		return widget
		
		
	
	def render(self, name, value, attrs=None):
		return mark_safe(self.get_recaptcha_widget_html(name))
		
class ReCaptchaResponseField(forms.Field):
	
	widget = ReCaptchaWidget
		
class ReCaptchaForm(forms.Form):
	recaptcha_response_field = ReCaptchaResponseField(label='Prove you are human')
	recaptcha_challenge_field = DummyField()
	
	def __init__(self, remoteip=None, *args, **kwargs):
		super(ReCaptchaForm, self).__init__(*args, **kwargs)
		self.remoteip = remoteip
                ''' This is a trick that makes the captcha the last field '''
		response_field = self.fields.pop('recaptcha_response_field')
		self.fields.insert(len(self.fields), 'recaptcha_response_field', response_field)
		
		
	
	def clean(self):
		cleaned_data = self.cleaned_data
		
		challenge = cleaned_data.get('recaptcha_challenge_field')
		response = cleaned_data.get('recaptcha_response_field')

		if challenge and response:
			result = self.verify_captcha(challenge, response)
			
			if not result:
				self._errors['recaptcha_response_field'] = self.error_class([u'Incorrect answer, try again.'])
				del cleaned_data['recaptcha_response_field']
			
		return cleaned_data
		
	def verify_captcha(self, challenge, response):
		url = 'http://www.google.com/recaptcha/api/verify'

		values = {
			'privatekey': settings.RECAPTCHA_PRIVKEY, 
			'remoteip' : self.remoteip, 
			'challenge' : challenge, 
			'response' : response
		}

		vrequest = urllib2.Request(url, urllib.urlencode(values))
		vresponse = urllib2.urlopen(vrequest)
		result = vresponse.read().split('\n')
		
		if result[0] == 'true':
			return True
			
		return False
		
		

More like this

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

Comments

pinkeen (on December 15, 2010):

Uups, the first line in ReCaptchaWidget.render is not needed.

#

pinkeen (on December 15, 2010):

^ Fixed

#

Please login first before commenting.