One of the must have elements on any html form as an anti-spam measure is a captcha, and the captcha technique that has worked pretty well for me is text based. So here’s how to implement one.
Create a controller and name it captcha
~$ paster controller captcha
Add this function to your helpers.py file
def makeCaptcha(lang):
label = {
1 : 'one',
2 : 'two',
3 : 'three',
4 : 'four',
5 : 'five',
6 : 'six',
7 : 'seven',
8 : 'eight',
9 : 'nine',
10 : 'ten'
}
num1 = random.randint(1,10)
num2 = random.randint(1,10)
res = num1 + num2
session['captcha'] = res
session.save()
return [label[num1],label[num2]]
On the templates folder create an html file with an example form
Save it and call it captcha.html
Add the following to captcha controller
I decided to create a validation schema using formencode:
import formencode
from formencode import variabledecode
from formencode import validators
from formencode.validators import Invalid, FancyValidator
#this class will validate the captcha value entered by the user
class CaptchaValidator(formencode.FancyValidator):
def _to_python(self,values,state):
if session.get('captcha') != int(values.get('captcha')):
raise formencode.Invalid(u"The math answer is incorrect",values, state)
return values
class NewInquiry(formencode.Schema):
allow_extra_fields = True
filter_extra_field = True
captcha = formencode.validators.String(
not_empty = True,
messages = {
'empty' : 'You need to answer the math question.'
})
# chain the captcha validator
chained_validators = [CaptchaValidator()]
# our contact view
def contact(self):
if request.method == 'POST':
try:
values = dict(request.params)
schema = NewInquiry()
results = schema.to_python(values)
except Invalid, e:
#raise error if something went wrong
else:
#send to contacts
return render('captcha.html')
And that’s pretty much it. The only drawback I can find is that isn’t very intuitive at times since users my try to answer with words instead of numeric values. That’s why I added a maxlength of 2 characters to the input field.
But you could easily implement a Javascript validation function to notify the user to type numeric values instead of characters before she submits the form.