Opened 15 years ago
Closed 13 years ago
#11112 closed New feature (fixed)
Formsets not supported as steps in FormWizard
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | contrib.formtools | Version: | dev |
Severity: | Normal | Keywords: | FormWizard FormSet wizard |
Cc: | andy@…, santtu@…, jashugan@…, stuff4riqui@…, nils@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | yes | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
A FormSet may not be included in FormWizard.form_list, since the individual forms will not be rendered to hidden fields on the next step, and the Wizard lacks machinery to handle them, producing assorted validation and hash errors.
I've written a patch to handle FormSets in FormWizard.form_list
Attachments (2)
Change History (23)
by , 15 years ago
Attachment: | wizard.patch added |
---|
comment:1 by , 15 years ago
milestone: | 1.1 → 1.2 |
---|---|
Triage Stage: | Unreviewed → Accepted |
Not a 1.1 feature.
comment:2 by , 15 years ago
Cc: | added |
---|
by , 15 years ago
Attachment: | wizard.patch2 added |
---|
Fixed case where formset is not last item in wizard
comment:3 by , 15 years ago
Cc: | added |
---|
comment:4 by , 15 years ago
Cc: | added |
---|
comment:5 by , 15 years ago
Cc: | added |
---|
comment:6 by , 15 years ago
milestone: | 1.2 |
---|
1.2 is feature-frozen, moving this feature request off the milestone.
comment:7 by , 14 years ago
Cc: | added |
---|
comment:8 by , 14 years ago
I tried to use the patch2 but I got this error:
I don't know anything about formset internals so I dunno how to solve it
Environment: Request Method: POST Request URL: http://localhost:8000/curricula/inserisci-candidato-e-curriculum/ Django Version: 1.2.3 Python Version: 2.6.6 Installed Applications: ['django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.admin', 'django.contrib.sites', 'hrar'] Installed Middleware: ('django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware') Traceback: File "/home/visi/ARcur/parts/django/django/core/handlers/base.py" in get_response 100. response = callback(request, *callback_args, **callback_kwargs) File "/home/visi/ARcur/parts/django/django/utils/decorators.py" in _wrapper 21. return decorator(bound_func)(*args, **kwargs) File "/home/visi/ARcur/parts/django/django/utils/decorators.py" in _wrapped_view 76. response = view_func(request, *args, **kwargs) File "/home/visi/ARcur/parts/django/django/utils/decorators.py" in bound_func 17. return func(self, *args2, **kwargs2) File "/home/visi/ARcur/HRAR/formSetWizard.py" in __call__ 67. form = self.get_form(next_step) File "/home/visi/ARcur/parts/django/django/contrib/formtools/wizard.py" in get_form 47. return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None)) File "/home/visi/ARcur/parts/django/django/forms/formsets.py" in __init__ 47. self._construct_forms() File "/home/visi/ARcur/parts/django/django/forms/formsets.py" in _construct_forms 97. self.forms.append(self._construct_form(i)) File "/home/visi/ARcur/parts/django/django/forms/formsets.py" in _construct_form 109. defaults['initial'] = self.initial[i] Exception Type: KeyError at /curricula/inserisci-candidato-e-curriculum/ Exception Value: 0
comment:10 by , 14 years ago
Keywords: | wizard added |
---|
comment:11 by , 14 years ago
I hit my same error again so I wrote here down for me and as a solution for anyone who could it this:
Be Aware of initial if improperly set could break the wizard at a FormSet Step.
comment:12 by , 14 years ago
This patch is marked as
Triage Stage: Accepted
Has patch: yes
Needs documentation: no
Needs tests: no
Patch needs improvement: no
but it's not in trunk
will it be merged ?
follow-up: 14 comment:13 by , 14 years ago
Needs tests: | set |
---|
It may well be marked "needs no tests" ,but a casual inspection of the patch demonstrates that this isn't the case. The patch doesn't contain tests.
comment:14 by , 14 years ago
Replying to russellm:
It may well be marked "needs no tests" ,but a casual inspection of the patch demonstrates that this isn't the case. The patch doesn't contain tests.
:)
follow-up: 19 comment:16 by , 14 years ago
hi anonymous :)
here there is an updated version based on the second patch
it's not a patch but a subclass of the current formWizard present on 1.2.4
there is no tests and probably initials doesn't work very well but I use it right now and it works
from django.contrib.formtools.wizard import FormWizard as fw from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_protect from django import forms class FormWizard(fw): @method_decorator(csrf_protect) def __call__(self, request, *args, **kwargs): """ Main method that does all the hard work, conforming to the Django view interface. """ if 'extra_context' in kwargs: self.extra_context.update(kwargs['extra_context']) current_step = self.determine_step(request, *args, **kwargs) self.parse_params(request, *args, **kwargs) # Sanity check. if current_step >= self.num_steps(): raise Http404('Step %s does not exist' % current_step) # Process the current step. If it's valid, go to the next step or call # done(), depending on whether any steps remain. if request.method == 'POST': form = self.get_form(current_step, request.POST) else: form = self.get_form(current_step) if form.is_valid(): # 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. # It is also possible that validation might fail under certain # attack situations: an attacker might be able to bypass previous # stages, and generate correct security hashes for all the # skipped stages by virtue of: # 1) having filled out an identical form which doesn't have the # validation (and does something different at the end), # 2) or having filled out a previous version of the same form # which had some validation missing, # 3) or previously having filled out the form when they had # more privileges than they do now. # # Since the hashes only take into account values, and not other # other validation the form might do, we must re-do validation # now for security reasons. current_form_list = [self.get_form(i, request.POST) for i in range(current_step)] for i, f in enumerate(current_form_list): if issubclass(f.__class__, forms.formsets.BaseFormSet): if request.POST.get("hash_%d" % i, '') != self.security_hash(request, f.management_form): return self.render_hash_failure(request, i) for ff in f.forms: if request.POST.get("hash_%d_%s" % (i, ff.prefix)) != self.security_hash(request, ff) : return self.render_hash_failure(request, i) else: if request.POST.get("hash_%d" % i, '') != self.security_hash(request, f): return self.render_hash_failure(request, i) if not f.is_valid(): return self.render_revalidation_failure(request, i, f) else: self.process_step(request, f, i) # Now progress to processing this step: self.process_step(request, form, current_step) next_step = current_step + 1 if next_step == self.num_steps(): return self.done(request, current_form_list + [form]) else: form = self.get_form(next_step) self.step = current_step = next_step return self.render(form, request, current_step) def render(self, form, request, step, context=None): "Renders the given Form object, returning an HttpResponse." old_data = request.POST prev_fields = [] if old_data: hidden = forms.HiddenInput() # Collect all data from previous steps and render it as HTML hidden fields. for i in range(step): old_form = self.get_form(i, old_data) hash_name = 'hash_%s' % i #handle formsets if issubclass(old_form.__class__,forms.formsets.BaseFormSet): # do management form and generate hash prev_fields.extend([bf.as_hidden() for bf in old_form.management_form]) prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form.management_form)))) for f in old_form.forms: # do each form and generate a hash for each hash_name = 'hash_%s_%s' % (i,f.prefix) prev_fields.extend([bf.as_hidden() for bf in f]) prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, f)))) else: prev_fields.extend([bf.as_hidden() for bf in old_form]) prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form)))) return self.render_template(request, form, ''.join(prev_fields), step, context)
follow-up: 18 comment:17 by , 14 years ago
Thank you, im anonymous.
No works for me :(
I add forms by using javascript. And change the variable TOTAL_FORMS. The validation does not work.
Thanks anyway
comment:18 by , 14 years ago
Replying to b3ni <camontuyu@…>:
Thank you, im anonymous.
No works for me :(
I add forms by using javascript. And change the variable TOTAL_FORMS. The validation does not work.
Thanks anyway
If you post the code to some pastebin site and the error I'll try to fix the problem
comment:19 by , 14 years ago
Replying to visik7:
This code works perfectly for me, within a subclass of Django's FormWizard, which is really nice as that's the standard workflow anyway.
Thank you very much!
comment:20 by , 13 years ago
Severity: | → Normal |
---|---|
Type: | → New feature |
comment:21 by , 13 years ago
Easy pickings: | unset |
---|---|
Resolution: | → fixed |
Status: | new → closed |
Superseded by #9200.
Handle Formsets in FormWizard