- Author:
- btaylordesign
- Posted:
- September 8, 2011
- Language:
- Python
- Version:
- 1.3
- Score:
- 1 (after 1 ratings)
This math captcha field and widget was inspired by Justin Quick's django-math-captcha, but I wanted to make it into one form field and not have anything in settings.py. I removed the division and modulo operators to avoid people having to input fractions, and just randomly select the operator.
It leverages Django's built-in MultiValueField to set up the hidden hashed answer to compare the user's input to instead of rendering strings for the fields like django-math-captcha does.
Unit tests soon to follow, but this is used in production at: http://btaylorweb.com/. Enjoy!
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 | from binascii import hexlify, unhexlify
from random import randint, choice
from django.conf import settings
from django.core.exceptions import ValidationError
from django import forms
from django.forms.fields import MultiValueField, IntegerField, CharField
from django.template.defaultfilters import mark_safe
from django.utils.hashcompat import sha_constructor
class MathCaptchaWidget(forms.MultiWidget):
def __init__(self, attrs=None):
self.attrs = attrs or {}
widgets = (
forms.TextInput(attrs={'size' : '5'}), #this is the answer input field
forms.HiddenInput() #this is the hashed answer field to compare to
)
super(MathCaptchaWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
"""
Split the initial value set by the field that implements
this field and return the double. These values get bound
to the fields.
"""
question, hashed_answer = value.split('|')
return [question, hashed_answer]
return [None, None]
def format_output(self, rendered_widgets):
return u'%s%s' % (rendered_widgets[0], rendered_widgets[1])
class MathCaptchaField(MultiValueField):
widget = MathCaptchaWidget
def __init__(self, start_int=0, end_int=10, *args, **kwargs):
#set up error messages
errors = self.default_error_messages.copy()
if 'error_messages' in kwargs:
errors.update(kwargs['error_messages'])
localize = kwargs.get('localize', False)
#set integers for question
x = randint(start_int, end_int)
y = randint(start_int, end_int)
#avoid negatives
if y > x:
x, y = y, x
#set up question
operator = choice('+,-,*'.split(','))
question = '%i %s %i' % (x, operator, y)
#make multiplication operator more human-readable
operator_for_label = '×' if operator == '*' else operator
#set label for field
kwargs['label'] = mark_safe('What is %i %s %i' % (x, operator_for_label, y))
#hash answer and set initial value of form
hashed_answer = sha_constructor(settings.SECRET_KEY + \
question).hexdigest() + hexlify(question)
kwargs['initial'] = '%s|%s' % ('', hashed_answer)
#set fields
fields = (
IntegerField(min_value=0, localize=localize),
CharField(max_length=255)
)
super(MathCaptchaField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
"""Compress takes the place of clean with MultiValueFields"""
if data_list:
answer = data_list[0]
#unhash and eval question. Compare to answer.
unhashed_answer = eval(unhexlify(data_list[1][40:]))
if answer != unhashed_answer:
raise ValidationError(u'Please check your math and try again.')
return answer
return None
###
Example usage:
from django import forms
from math_captcha_field import MathCaptchaField
class ContactForm(forms.Form):
name = forms.CharField(max_length=75)
#...
captcha = MathCaptchaField(required=True)
`Optionally, MathCaptchaField has parameters for
the starting and ending integers for the range of
random numbers to select from. Defaults are:
start_int=10, end_int=10.`
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.