FormWizard and clean

133 views
Skip to first unread message

Greig Rapley

unread,
Apr 4, 2008, 7:06:49 AM4/4/08
to Django users
Hi,

I am using a FormWizard with two simple forms. The first form has some
complex validation to do on two of the fields, so I put that code in
clean(). That all works fine.

After the second form has been submitted, it appears that the clean()
is called on the first form again. This happens before the done()
method on the Wizard itself. Obviously with an expensive (time)
validation I don't want to do it twice. Is there some way to do once
only validation on the first form in a FormWizard ?

Thanks,
Greig

Tim Chase

unread,
Apr 4, 2008, 7:33:04 AM4/4/08
to django...@googlegroups.com

In general, you want to validate every datum on every request.
This prevents people from modifying the hidden fields used to
hold the 1st form when they submit the 2nd form. However, as you
describe, it might not be feasible if some complex calculation
occurs. A couple possibilities that occur to me:

You could only clean the 1st form if you're coming from the 2nd
form (skip cleaning on the 1st form). That saves you from doing
it the 1st time rather than the 2nd time. However, if your 2nd
form depends on trusting information in the first form, this
won't work.

Alternatively, you could add a secured MD5/SHA1 hash to the be
included in the hidden fields that verifies that the given data
has been cleaned already. You would combine all your fields, a
salt, and a secret in a predictable order, and then get the
MD5/SHA1 of that content. Then instead of whatever your
complicated check is, you can just recombine your fields, your
salt, and your secret (in the same order), and check the MD5/SHA1
hash across them. If they match, all is good. If they don't
match, the user has altered the hidden form data and you can
either error out, or do the recalculation of the first form.

-tim


Greig Rapley

unread,
Apr 4, 2008, 9:01:00 AM4/4/08
to Django users
Thanks Tim, I'll take your encrypted hash suggestion and go with that.

This will require that I edit django.contrib.formtools.wizard.py
though, won't it ?
As this "check the hidden encrypted hash" should take place instead of
line 86, which just does form.is_valid() again ?
Should I be trying to do this in a way that can be committed back to
Django, or is this something that is "not recommended" ?

Thanks,
Greig

Greig Rapley

unread,
Apr 4, 2008, 10:04:59 AM4/4/08
to Django users
On closer inspection, isn't your alternative inspection taken care of
by the security_hash(...) method ? In which case, all I want is for
revalidation to be skipped ? Can't I simply add a method, which could
be overridden in subclasses which says;

def revalidation():
return True

And wrap the revalidation logic in a conditional using this method (in
django/contrib/formtools/wizard.py);

# Validate all the forms. If any of them fail validation,
that
# must mean the validator relied on some other input, such
as
# an external Web site.
# ADDING THE FOLLOWING LINE
if self.revalidate():
for i, f in enumerate(final_form_list):
if not f.is_valid():
return
self.render_revalidation_failure(request, i, f)
return self.done(request, final_form_list)

And then my (and others) subclasses can simply implement revalidate to
return False ?

Thoughts ?

Thanks,
Greig


On Apr 4, 12:33 pm, Tim Chase <django.us...@tim.thechases.com> wrote:

Honza Král

unread,
Apr 4, 2008, 4:08:50 PM4/4/08
to django...@googlegroups.com
Hi,

all the forms are validated before passed to the done() method so that
it can expect cleaned data to be present on all of them.

in every step, only the currently submitted form is validated, just in
the last one, before done() is called, every single form is cleaned so
that done() doesn't have to do this.

Originally this wasn't there but I found out that every single
subclass of Wizard I wrote had done() starting in the same way -
cleaning all the forms so that I could access cleaned_data.

does this answer your question?

--
Honza Král
E-Mail: Honza...@gmail.com
ICQ#: 107471613
Phone: +420 606 678585

Greig Rapley

unread,
Apr 4, 2008, 6:31:26 PM4/4/08
to Django users
Thanks for the explanation. I think I am trying to misuse FormWizard
and that is why I am having trouble. Basically I have a 2 form
wizard. The first form asks for 4 required fields. Two of those
fields are username and password for another (related) website. What
I was trying to do was to was, in the clean() method for that form,
actually use the entered username and password to login to the other
website, to check that they were in fact 'valid'. Unfortunately this
process can take 20 - 30 seconds. While that is acceptable once, it
isn't really acceptable, or necessary to do it again after submitting
the second form. I couldn't work out how to do this clean only once,
using the hooks provided. So I tried to move them into process_step,
and then from there use extra_context to store a flag saying whether
or not the username and password had been verified. That way the
clean can still be performed twice on the first form, as it no longer
does the expensive check. The flag can be used in process_step to
ensure it only gets done once. The trouble I now have is that I can't
control the flow of FormWizard in the event that the check done in
process_step() fails. At least not without modifying wizard.py :-
( If you think that what I am trying to do is outside the scope of
FormWizard then maybe I should just code up a custom two-form signup.
However if you could see a way I could do this with FormWizard, I
would appreciate your help !

Thanks is advance,

Greig
> E-Mail: Honza.K...@gmail.com

Honza Král

unread,
Apr 4, 2008, 6:50:46 PM4/4/08
to django...@googlegroups.com
you could probably store the information in the session after the user
has been validated and in parse_params, move this information onto the
form which would then skip this step when called before done()...

but it may be easier just to do it yourself in a view without the Wizard... ;)

--
Honza Král
E-Mail: Honza...@gmail.com

Reply all
Reply to author
Forward
0 new messages