Adding errors into the form?

19 views
Skip to first unread message

FX

unread,
Jan 5, 2006, 10:43:56 PM1/5/06
to Django users
Folks,
I'm working on an application that makes use of Django Captcha. I want
to be able to display the captcha validation error along with other
errors on the form after post, and here is my view code (please note
the story model is the same as django.models.storys so the manipulator
is django.models.storys.StoryManipulatorAdd):

def create_story(request, captcha_error):
manipulator = storys.AddManipulator()

if request.POST:
new_data = request.POST.copy()
new_data['submission_user'] = str(request.user.id)
errors = manipulator.get_validation_errors(new_data)
if captcha_error:
errors['captcha_word'] = captcha_error
manipulator.do_html2python(new_data)

if not errors:
new_story = manipulator.save(new_data)

return HttpResponseRedirect('/category/' + new_data['category_id'])
else:
new_data = manipulator.flatten_data()
errors = {}

form = formfields.FormWrapper(manipulator, new_data, errors)
#debug
print form
return render_to_response('faxian/create_form', {'form': form},
context_instance=Context(request))

Below is the related template:
{% load captcha %}

{% captcha %}
<div id='captcha'>
{% trans "Please enter the word you see in the picture" %} {% if
form.captcha_word.errors %}*** {{ form.captcha_word.errors|join:", "
}}<input type="text" name="{{captcha_input_name}}"/>
<input type="hidden" name="{{captcha_hidden_input_name}}"
value="{{captcha_id}}"/><input type="submit" name="submit" value="{%
trans 'submit' %}" /><br /><img src={{captcha_image}} width=150
height=60 />
</div>


When I print out the form for debugging, I do see the following:
{'error_dict': {'captcha_word': ['Invalid word.']}, 'manipulator':
<django.models.storys.StoryManipulatorAdd instance at 0x01797350>, ...}

but it seems from.captcha_word.errors doesn't get the value out of
form, as the error message is not displayed on the browser.

Any clue would be appreciated!

--Feiyu

Ian Holsman

unread,
Jan 5, 2006, 10:51:41 PM1/5/06
to django...@googlegroups.com
Hi Feiyu.

the problem is the captcha validation is the manipulator out of scope
when the the validation is run.

there are 2 choices.
1. the captcha view should throw a validation error (not a 404)..
thats my bug, and I will fix it shortly. This will mean you will get
the error message on your edit page.

2. the 2nd is to integrate the captcha code into your view itself. in
order to do this cleanly I should refactor the code so you have a
function to call something like

is_valid_captcha( request.POST )
and let you create you own validation error.

I'll try to get around to it tomorrow.


--
I...@Holsman.net -- blog: http://feh.holsman.net/ -- PH: ++61-3-9877-0909

If everything seems under control, you're not going fast enough. -
Mario Andretti

FX

unread,
Jan 6, 2006, 12:29:32 AM1/6/06
to Django users
Hi Ian,
Thanks for the prompt reply. (which time zone are you in?)

I made some modifications to your Django Captcha that might be useful,
so I'm attaching the patch (generated by svn) below. The changes are:
1. modified to make use of tempfile.mkdtemp() and tempfile.tempdir
instead of "/tmp" to make it OS/platform argnostic.
2. modified the captcha_class to set context variables instead of
returning hard coded html code, so the template author can use it with
more flexibility
3. added an empty __init.py__ under zilbo/ so I can use the captcha app
by refering it as zilbo.common.captcha

patch:


Index: __init__.py
===================================================================
--- common/captcha/views.py (revision 58)
+++ common/captcha/views.py (working copy)
@@ -1,3 +1,4 @@
+import tempfile
from django.core.exceptions import Http404, ObjectDoesNotExist,
ViewDoesNotExist
from django.core.extensions import DjangoContext as Context
from django.conf.settings import SITE_ID
@@ -8,7 +9,7 @@
import Captcha

def _getFactory( id ):
- return Captcha.PersistentFactory("/tmp/pycaptcha_%d" % id )
+ return Captcha.PersistentFactory(tempfile.gettempdir() +
"/pycaptcha_%d" % id )

def image( request ):
"""
@@ -29,21 +30,25 @@
verify the captcha and then forward the request
TBD: redirect to the original form with a validation error
"""
+
+ captcha_error = []
+
if request.POST:
id = request.POST["captcha_id"]
word = request.POST["captcha_word"]
test = _getFactory(SITE_ID).get(id)
if not test:
- raise Http404("invalid captcha id")
+ captcha_error.append('Invalid captcha id.')
if not test.valid:
- raise Http404("Test invalidated, try again")
+ captcha_error.append('Test invalidated, try again.')
elif not test.testSolutions([word]):
- raise Http404("try again")
+ captcha_error.append('Invalid word.')
+
mod_name, func_name = urlresolvers.get_mod_func(forward_to)

try:
func, ignore = getattr(__import__(mod_name, '', '', ['']),
func_name), {}
- return func( request, *arguments, **keywords)
+ return func(request, captcha_error, *arguments, **keywords)
except (ImportError, AttributeError), e:
raise ViewDoesNotExist, "Tried %s. Error was: %s" %
(forward_to, str(e))

@@ -52,21 +57,26 @@
verify the captcha and then forward the request for anonymous
users only
TBD: redirect to the original form with a validation error
"""
+ captcha_error = []
+
if request.POST and request.user.is_anonymous():
- id = request.POST["captcha_id"]
- word = request.POST["captcha_word"]
- test = _getFactory(SITE_ID).get(id)
- if not test:
- raise Http404("invalid captcha id")
- if not test.valid:
- raise Http404("Test invalidated, try again")
- elif not test.testSolutions([word]):
- raise Http404("try again")
+
+ if request.POST:
+ id = request.POST["captcha_id"]
+ word = request.POST["captcha_word"]
+ test = _getFactory(SITE_ID).get(id)
+ if not test:
+ captcha_error.append('Invalid captcha id.')
+ if not test.valid:
+ captcha_error.append('Test invalidated, try again.')
+ elif not test.testSolutions([word]):
+ captcha_error.append('Invalid word.')
+
mod_name, func_name = urlresolvers.get_mod_func(forward_to)

try:
func, ignore = getattr(__import__(mod_name, '', '', ['']),
func_name), {}
- return func( request, *arguments, **keywords)
+ return func( request, captcha_error, *arguments, **keywords)
except (ImportError, AttributeError), e:
raise ViewDoesNotExist, "Tried %s. Error was: %s" %
(forward_to, str(e))

Index: common/captcha/templatetags/captcha.py
===================================================================
--- common/captcha/templatetags/captcha.py (revision 58)
+++ common/captcha/templatetags/captcha.py (working copy)
@@ -1,3 +1,4 @@
+import tempfile
from django.core import template
from django.conf.settings import SITE_ID
import Captcha
@@ -3,9 +4,26 @@
from Captcha.Visual import Tests

+tempfile.mkdtemp()
+
+#TODO change this to be generic
def _getFactory(id):
- return Captcha.PersistentFactory("/tmp/pycaptcha_%d" % id )
+ return Captcha.PersistentFactory(tempfile.gettempdir() +
"/pycaptcha_%d" % id )

+#TODO change the tag to be more generic so we can customize the
language and the size of the image, etc
class captcha_class(template.Node):
- """ return a image and a input box """
+ """ generate a captcha image and specify the related input box
parameters.
+
+ Basically you have the following context variables:
+ captcha_image: the url to the captcha image
+ captcha_input_name: the input box name to submit the captcha
word
+ captcha_hidden_input_name: the hidden input box name to submit
the captcha id
+ captcha_id: captcha id, for the hidden input box's content
+ You will need to submit both the captcha word and the captcha id
in your form for validation. Sample code:
+ <div id='captcha'>
+ {% trans "Please enter the word you see in the picture"
%}<input type="text" name="{{captcha_input_name}}"/>
+ <input type="hidden" name="{{captcha_hidden_input_name}}"


value="{{captcha_id}}"/><input type="submit" name="submit" value="{%
trans 'submit' %}" /><br />

+ <img src={{captcha_image}} width=150 height=60 />
+ </div>
+ """
def __init__(self, anon_users_only):
self.anon_users_only = anon_users_only
@@ -17,18 +35,25 @@
return ""
name = Tests.__all__[0]
test = _getFactory(SITE_ID).new(getattr(Tests, name))
- return """
-<div id='catpcha'>
-<p><img src='/captcha/i/?id=%s' /></p>
-Type in the word you see in the picture <input type="text"
name="captcha_word"/>
-<input type="hidden" name="captcha_id" value="%s"/>
-</div>
- """ % ( test.id, test.id )
+
+ context['captcha_image'] = '/captcha/i/?id=%s' % test.id
+ context['captcha_input_name'] = "captcha_word"
+ context['captcha_hidden_input_name'] = "captcha_id"
+ context['captcha_id'] = test.id
+ return ''
+# """
+#<div id='catpcha'>
+#<p><img src='/captcha/i/?id=%s' /></p>
+#Type in the word you see in the picture <input type="text"
name="captcha_word"/>
+#<input type="hidden" name="captcha_id" value="%s"/>
+#</div>
+# """ % ( test.id, test.id )

def captcha(parser, token):
"""
{% captcha %}
"""
+
return captcha_class(False)

def captcha_anon(parser, token):

Ian Holsman

unread,
Jan 8, 2006, 4:53:23 AM1/8/06
to django...@googlegroups.com
On 1/6/06, FX <feiy...@gmail.com> wrote:
>
> Hi Ian,
> Thanks for the prompt reply. (which time zone are you in?)
I live in Melbourne Australia.

>
> I made some modifications to your Django Captcha that might be useful,
> so I'm attaching the patch (generated by svn) below. The changes are:
> 1. modified to make use of tempfile.mkdtemp() and tempfile.tempdir
> instead of "/tmp" to make it OS/platform argnostic.
> 2. modified the captcha_class to set context variables instead of
> returning hard coded html code, so the template author can use it with
> more flexibility
> 3. added an empty __init.py__ under zilbo/ so I can use the captcha app
> by refering it as zilbo.common.captcha
>
> patch:
>

Applied.

Thanks for the patch!

Reply all
Reply to author
Forward
0 new messages