Opened 17 years ago

Closed 16 years ago

Last modified 16 years ago

#3218 closed enhancement (fixed)

[patch] django.contrib.formtools.wizard proposal

Reported by: Honza Král <Honza.Kral@…> Owned by: Adrian Holovaty
Component: contrib.formtools Version: dev
Severity: normal Keywords: newforms wizard sprintsept14
Cc: Honza.Kral@…, nick.lane.au@…, larlet@…, jesse.lovelace@…, allandouglas@…, remco@…, matt.dorn@…, join.together@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

following the discussion
http://groups-beta.google.com/group/django-developers/browse_thread/thread/d500c47eb51353e3/2e61103c43fba506?

I have added some more flexibility and cleaned up the code a bit (and added comments ;) ).

Features:

  • just subclass the wizard.Wizard class, override the done() method and you are done - you can just point your urls.py to MyWizard( [list, of, newform, classes] ) and wait...
  • data from previous steps are stored in hidden fields and passed back and forth (do not try and use this for uploading files in any, but the last step of the form ! ), NO data is stored on the server
  • security hash is calculated once the form is validated ( thus you avoid validating each form every time and can detect manual tampering with the POST data)

If that's not enough you can (by overriding/extending some methods):

  • create the list of forms dynamically (you still have to supply at least one form, so that the wizard has something to start with) - do this in process_step() (just append to self.form_list ) -- see the last email for example
  • use a custom template name for every step ( get_template() )
  • override the rendering method for custom rendering ( render_template() )
  • override the mechanism that handles the step counter - by default that is in POST['wizard_step'] , but you can put it in your URL, GET or even session if you wish ( parse_params() )
  • provide extra_context (from urls.py, process_step(), parse_params() etc...)

The only thing that is pretty much hardcoded is the use of POST throughout the wizard.

I am using the code (not the newest version, but I only made cosmeticall changes since) for some time now and everything works.

Once I get some feedback and a few people try it out, I will put together some documentation.

Attachments (13)

wizard.py (7.5 KB ) - added by Honza Král <Honza.Kral@…> 17 years ago.
wizard_20070111.py (7.8 KB ) - added by Honza Král <Honza.Kral@…> 17 years ago.
updated version
wizard.2.py (8.3 KB ) - added by Honza Král 17 years ago.
matching current trunk (6158)
wizard.3.py (8.1 KB ) - added by Honza Král 17 years ago.
matching django's coding style
form_wizard_start_of_tests.diff (13.4 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
My test code so far
wizard.4.py (8.3 KB ) - added by Honza Král 17 years ago.
validating all forms before invoking done()
form_wizard_start_of_tests.2.diff (12.1 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
Test so far, anyone else want to give it a go, i'm suffering from test blindness
wizard.5.py (8.5 KB ) - added by Honza Král 17 years ago.
added current_step and step_count
form_wizard_start_of_tests_using_wizard.5.diff (13.3 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
Updated to Honza's latest code
form_wizard_hash_tests.diff (13.9 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
Test of hashes done
form_wizard_hash_tests_failed_hash_dynamic_form.diff (16.6 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
Added some extra tests
form_wizard.txt (3.2 KB ) - added by mdorn 17 years ago.
Updated documentation, final.
form_wizard_with_tests_and_docs_r6199.diff (20.1 KB ) - added by Øyvind Saltvik <oyvind@…> 17 years ago.
Complete patch for newforms-admin branch

Download all attachments as: .zip

Change History (43)

by Honza Král <Honza.Kral@…>, 17 years ago

Attachment: wizard.py added

comment:1 by jl@…, 17 years ago

Very nice Honza,
I'm already using it in a registration form.

What do you think about a method to navigate through the formlist with html inputs like [< Back] [ Next Step >]?
Or is it possible with current wizard.py (01/02/07)?

comment:2 by Honza Král <Honza.Kral@…>, 17 years ago

yes it is, you just press the back button, it will work, or you revert the step to a lower number... but these are all hacks, there is no currently supported way...

I have new version that allows the form to be pre-filled from the previous steps... I will attach it...

by Honza Král <Honza.Kral@…>, 17 years ago

Attachment: wizard_20070111.py added

updated version

comment:3 by Jannis Leidel <jl@…>, 17 years ago

thanks for the initial data function..
unfortunately I don't understand how to use it. could you give a small example like you did on the maillinglist?

regarding the navigational functionality with "back"- and "next"-form submit inputs:

I try to lower self.step within a subclass method "parse_params" by finding out if request.POST.get("navigation") is set (submit input html tag with the name "navigation") and decreasing self.step by 1. strangely it will just work if I decrease by 2 which then breaks the parsing of the template when I want to jump back to form 0 because it wants template wizard-1.html and not wizard0.html. I'm sure this is just an misunderstanding on my side. could you explain a little more?

comment:4 by nervotrepka, 17 years ago

Component: django.contrib.formtoolsUnit test system
Severity: normalblocker
Version: SVNnew-admin

5cb16d1ade0a My homepage vvv_5_1 vvv_5_1
vvv_5_2 vvv_5_2
vvv_5_3 vvv_5_3
vvv_5_4 vvv_5_4
vvv_5_5 vvv_5_5

comment:5 by Collin Grady <cgrady@…>, 17 years ago

Component: Unit test systemdjango.contrib.formtools
Severity: blockernormal
Version: new-adminSVN

comment:6 by Honza Král <Honza.Kral@…>, 17 years ago

Needs documentation: set

comment:7 by Simon G. <dev@…>, 17 years ago

Triage Stage: UnreviewedDesign decision needed

comment:8 by nick.lane.au@…, 17 years ago

Cc: nick.lane.au@… added

comment:9 by anonymous, 17 years ago

Cc: larlet@… added

comment:10 by anonymous, 17 years ago

Cc: jesse.lovelace@… added

comment:11 by anonymous, 17 years ago

Cc: allandouglas@… added

comment:12 by Honza Král, 17 years ago

Owner: changed from nobody to Honza Král

comment:13 by David Larlet, 17 years ago

There is an interesting snippet about that: http://www.djangosnippets.org/snippets/222/

comment:14 by anonymous, 17 years ago

Cc: remco@… added

by Honza Král, 17 years ago

Attachment: wizard.2.py added

matching current trunk (6158)

comment:15 by Adrian Holovaty, 17 years ago

Triage Stage: Design decision neededAccepted

This is a cool idea. Before we can check it in, though, it needs docs and tests.

comment:16 by mdorn, 17 years ago

Owner: changed from Honza Král to mdorn
Status: newassigned

working on docs and tests

comment:17 by Øyvind Saltvik <oyvind@…>, 17 years ago

Keywords: sprintsept14 added

by Honza Král, 17 years ago

Attachment: wizard.3.py added

matching django's coding style

by Øyvind Saltvik <oyvind@…>, 17 years ago

My test code so far

by Honza Král, 17 years ago

Attachment: wizard.4.py added

validating all forms before invoking done()

by Øyvind Saltvik <oyvind@…>, 17 years ago

Test so far, anyone else want to give it a go, i'm suffering from test blindness

by Honza Král, 17 years ago

Attachment: wizard.5.py added

added current_step and step_count

by Øyvind Saltvik <oyvind@…>, 17 years ago

Updated to Honza's latest code

by Øyvind Saltvik <oyvind@…>, 17 years ago

Attachment: form_wizard_hash_tests.diff added

Test of hashes done

by Øyvind Saltvik <oyvind@…>, 17 years ago

Added some extra tests

by mdorn, 17 years ago

Attachment: form_wizard.txt added

Updated documentation, final.

comment:18 by mdorn, 17 years ago

Cc: matt.dorn@… added
Owner: changed from mdorn to Honza Král
Status: assignednew

Documentation finished, reassigning to Honza, so he can double-check before moving to check-in

comment:19 by mdorn, 17 years ago

Needs documentation: unset

by Øyvind Saltvik <oyvind@…>, 17 years ago

Complete patch for newforms-admin branch

comment:20 by jstritar@…, 16 years ago

Since the previous step's data is kept as POST parameters, there isn't a nice way to go back to a previous step (other than the back button). I think something like this is an important usability feature (think of a checkout process where you can click previous steps if you made a mistake).

Example:

| 1 | 2 | 3 | 4 |

I am on step 4. I click the 2, loading the 2nd step with the data I originally entered.

in reply to:  20 comment:21 by Honza Král, 16 years ago

Replying to jstritar@gmail.com:

Since the previous step's data is kept as POST parameters, there isn't a nice way to go back to a previous step (other than the back button). I think something like this is an important usability feature (think of a checkout process where you can click previous steps if you made a mistake).

Example:

| 1 | 2 | 3 | 4 |

I am on step 4. I click the 2, loading the 2nd step with the data I originally entered.

I thought that wouldn't be the common usage, but you can achieve that via overriding the parse_params method, you can derive self.step from URL, GET or any other means you feel like...

comment:22 by Jon Stritar <jstritar@…>, 16 years ago

You'd be able to pass the step via a URL or GET parameter, but the Forms up to and including that step wouldn't have any data (since there wasn't a POST).

in reply to:  22 ; comment:23 by Honza Král, 16 years ago

Replying to Jon Stritar <jstritar@gmail.com>:

You'd be able to pass the step via a URL or GET parameter, but the Forms up to and including that step wouldn't have any data (since there wasn't a POST).

true, haven't actually thought it through this far... I guess the only option is to have the form post the data with the appropriate step value - you won't avoid a form or some javascript...

I a generally opposed to the idea of storing the data on server-side it doesn't make much sense, adds a lot of additional complexity and dependency on sessions and/or some other form of storing the data...

in reply to:  23 ; comment:24 by anonymous, 16 years ago

Replying to Honza_Kral:

I a generally opposed to the idea of storing the data on server-side it doesn't make much sense, adds a lot of additional complexity and dependency on sessions and/or some other form of storing the data...

I think what we are talking about here is state, and isn't maintaining state exactly what sessions are for in an otherwise stateless protocol? I know that it is more elegant to minimize dependancies, but depending on the sessions framework is justified here imho.

in reply to:  24 comment:25 by remco@…, 16 years ago

oops didn't want to show as an anonymous coward :)

comment:26 by anonymous, 16 years ago

what is the status of this bug?

I tested the diffs with 0.97-pre-SVN-6971 and it didn't work for me. :(

comment:27 by Adrian Holovaty, 16 years ago

Owner: changed from Honza Král to Adrian Holovaty
Status: newassigned

I have an immediate need for this for a project, so I'll take the lead on reviewing and checking it in.

comment:28 by Adrian Holovaty, 16 years ago

Resolution: fixed
Status: assignedclosed

(In [7236]) Fixed #3218 -- Implemented django.contrib.formtools.wizard. Thanks, Honza and Oyvind. Note that there are no docs yet

comment:29 by anonymous, 16 years ago

Cc: join.together@… added

I know a lot of people have cc'd this, so i'll ask this here before making a ticket out of it in the hopes that someone can answer.

When calculating the hash, why dose it use unclean data? I've found a bug that causes Booleanfields that are set to false to erroneously cause the hash tst

(On the first pass, the value for the field is calculated to be (empty string), but the next time you try to submit something it's somehow False now, so the data is different than it was previously so the test fails and you have to start over.)

I would gladly write a patch that has it pull the data to be hashed from form.cleaned data, but before I do is there a reason why it doesn't already do that?

in reply to:  29 comment:30 by Honza Král, 16 years ago

Replying to anonymous:

I know a lot of people have cc'd this, so i'll ask this here before making a ticket out of it in the hopes that someone can answer.

When calculating the hash, why dose it use unclean data?

because than we can make tests without doing the clean(), which may be an expensive operation... so we don't have to validate every data on every pass...

Note: See TracTickets for help on using tickets.
Back to Top