Opened 13 years ago
Closed 12 years ago
#17850 closed New feature (wontfix)
Passing FormWizard.as_view(form_list) a dynamic form_list
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | contrib.formtools | Version: | 1.4-beta-1 |
Severity: | Normal | Keywords: | formwizard |
Cc: | kristoffer.snabb@… | Triage Stage: | Design decision needed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
The following technique fails to provide the expected behavior. The error returned is:
TypeError: issubclass() arg 1 must be a class # happens in /django/contrib/formtools/wizard/views.py", line 166, in get_initkwargs
urls.py:
from django.conf.urls import patterns, url # from views import survey_wizard from views import SurveyWizard from forms import get_survey_page_list # I'd rather do this dynamically as well, but this *should* work survey_page_list = get_survey_page_list('274a442b-67ec-11e1-b010-78ca39b1b597') urlpatterns = patterns('', url(r'^(?P<survey>[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})$', SurveyWizard.as_view(form_list=survey_page_list)), )
forms.py:
def get_survey_page_list(survey): """ Build a list of survey pages for the form wizard to use. args: survey - a unicode string passed in from a URL parameter """ survey_page_list = [] # Since we're receiving a captured URL parameter, we have to use that # string to acutally fetch the survey object. s = Survey.objects.get(pk=survey) for page in s.page_set.all().order_by('number'): step_name = unicode(page.number) form_class = make_survey_page_form(survey, page.number) survey_page_list.append((step_name, form_class)) return SortedDict(survey_page_list)
Looking into the Werkzeug debugger reveals that the functions called in forms.py are grabbing and returning a SortedDict of forms:
>>> dir() ['args', 'cls', 'condition_dict', 'form', 'form_list', 'i', 'init_form_list', 'initial_dict', ] >>> form_list django.utils.datastructures.SortedDict({u'1': <class 'mysite.survey.forms._SurveyPageForm'>, u'2': <class 'mysite.survey.forms._SurveyPageForm'>, u'3': <class 'mysite.survey.forms._SurveyPageForm'>}) >>> initial_dict >>> init_form_list django.utils.datastructures.SortedDict({u'0': u'1', u'1': u'2', u'2': u'3'})
Change History (8)
follow-up: 7 comment:1 by , 13 years ago
comment:2 by , 13 years ago
The problem is that the WizardView takes a form_list of classes and not object instances. I cannot say if this is a bug or a design choice, maybe a performance issue? Anyway I would also appreciate a possibility to create forms more dynamically.
Taken from WizardView:
if issubclass(form, formsets.BaseFormSet): # if the element is based on BaseFormSet (FormSet/ModelFormSet) # we need to override the form variable. form = form.form
comment:3 by , 13 years ago
Cc: | added |
---|
comment:4 by , 13 years ago
Yes, no matter what I did, WizardView was always expecting some kind of list. :)
I'm also not sure if this a bug, but I think dynamic forms is a feature worth having.
Here's what I went with, but the form wizard is still acting weird, not giving me a next button when there are multiple steps:
class SurveyWizard(CookieWizardView): def dispatch(self, request, *args, **kwargs): survey_page_list = get_survey_page_list(kwargs['survey']) self.form_list = self.get_initkwargs(survey_page_list)['form_list'] return super(SurveyWizard, self).dispatch(request, *args, **kwargs) def done(self, form_list, **kwargs): # do something
Also, I passed SurveyWizard.as_view() a list filled with a bogus form to avoid the wrong number of arguments error. The form list is overridden by dispatch above, anyway.
Thanks to SmileyChris on #django for walking me through the source for a few hours to figure this out.
comment:5 by , 13 years ago
Triage Stage: | Unreviewed → Design decision needed |
---|---|
Type: | Bug → New feature |
This isn't really a bug, to be honest. You're expecting a behavior that the form wizard wasn't written for. Marking as design decision needed.
comment:7 by , 12 years ago
Replying to jrhorn424:
Using a slightly different technique yields a different error.
TypeError: get_initkwargs() takes at least 2 arguments (1 given) # in "/django/django/contrib/formtools/wizard/views.py", line 118, in as_view
This happens because you're currently forced to provide a form_list.
views.py:
class SurveyWizard(CookieWizardView): def get_form_list(self): form_list = get_survey_page_list(self.kwargs['survey']) return form_list survey_wizard = SurveyWizard.as_view()I suspect this is because SurveyWizard.as_view() is expecting an argument to pass along to get_initkwargs.
Yes, you have to provide a form_list, but - as a quick fix - you could provide a dummy form list with one form. You could then override the get_form_list method as you already did and it should work.
What we need is a way to tell the wizard that the empty form_list is ok. For example with an attribute in a subclass "allow_empty_form_list = True".
comment:8 by , 12 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
As jezdez says, this isn't what the wizard is designed for, sorry.
Using a slightly different technique yields a different error.
#urls.py
views.py:
I suspect this is because SurveyWizard.as_view() is expecting an argument to pass along to get_initkwargs. Werkzeug:
However, I'm at a loss as to how else to get a dynamically generated list of forms into form_list.