I'm creating an app containing 3 multiple choice questions. The second multiple choice options are dependent on the response to the first question. To accomplish this, I added a "choices" argument to the second form and called that argument in the wizard's get_form function.
While everything appears to work normally, the problem occurs when trying to submit the form on the last step. The done() function is never called and neither is the clean() function in forms.py. The page just gets refreshed without any errors appearing and the existing selected options get wiped.
From testing, I've concluded that removing the custom init from forms.py and the get_form from views.py fixes the issue--but obviously defeats the purpose of this. I've been trying for a very long time to solve this issue but just can't seem to, any help is greatly apperciated!
views.py
class FormWizardView(SessionWizardView):
form_list = [FormReflectionOne, FormReflectionTwo]
template_name = 'reflection/form.html'
def done(self, form_list, **kwargs):
form_data = [form.cleaned_data for form in form_list]
# Save form_data to Reflection Model
return redirect('dashboard')
def get_form(self, step=None, data=None, files=None):
if step is None:
step = self.steps.current
if step == '1':
firststep_data = self.get_cleaned_data_for_step('0')
choices_list = AdjectiveChoice.objects.filter(feeling=firststep_data['feeling'])
choices = [(x.order, x.adjective) for x in choices_list]
form = FormReflectionTwo(choices)
else:
return super(FormWizardView, self).get_form(step, data, files)
return form
forms.py
class FormReflectionTwo(forms.Form):
def __init__(self, choices, *args, **kwargs):
super(FormReflectionTwo, self).__init__(*args, **kwargs)
self.fields['adjective'].choices = choices
def clean(self):
cleaned_data = super(FormReflectionTwo, self).clean()
adjective = cleaned_data.get('adjective')
reason = cleaned_data.get('reason')
if adjective and reason:
if len(adjective) > 3:
raise ValidationError({'adjective': "Pick 1 to 3 adjectives"})
if len(reason) > 3:
raise ValidationError({'reason': "Pick 1 to 3 reasons"})
return cleaned_data
adjective = forms.MultipleChoiceField(choices=ADJECTIVE_CHOICES, widget=forms.CheckboxSelectMultiple, required=True)
reason = forms.MultipleChoiceField(choices=REASON_CHOICES, widget=forms.CheckboxSelectMultiple, required=True)
class Meta:
model = ReflectionEntry
fields = ['adjective', 'reason']
Small other notes: ADJECTIVE_CHOICES variable is just a complete list of all adjective choices, I know it's not needed but it's there as a fallback. The AdjectiveChoice model holds all the possible options as well as what "question 1" answer they are meant for.
Thanks again!
If you look at the code for the get_form
method, there are a lot of set up that is done to prepare the form to work with the form wizard. In your case, I suspect that the form prefixes set up by the get_form_prefix
method that are messing the form wizard logic.
(Method 1)To achieve what you want, you can continue on the same thought of using get_form
, and set the choices attribute of the field after you get the form as suggested in the docs :
def get_form(self, step=None, data=None, files=None):
form = super().get_form(step, data, files)
# determine the step if not given
if step is None:
step = self.steps.current
if step == '1':
firststep_data = self.get_cleaned_data_for_step('0')
choices_list = AdjectiveChoice.objects.filter(feeling=firststep_data['feeling'])
choices = [(x.order, x.adjective) for x in choices_list]
form.fields['adjective'].choices = choices
return form
(Method 2)To achieve what you want, is to override the get_form_kwargs
method to pass the coices to your form init:
def get_form_kwargs(self, step=None):
"""
Returns the keyword arguments for instantiating the form
(or formset) on the given step.
"""
if step == "1":
firststep_data = self.get_cleaned_data_for_step('0')
choices_list = AdjectiveChoice.objects.filter(feeling=firststep_data['feeling'])
choices = [(x.order, x.adjective) for x in choices_list]
return {"choices": choices}
return {}
and you'll find them in your form init in the kwargs
:
forms.py
class FormReflectionTwo(forms.Form):
def __init__(self, *args, **kwargs):
super(FormReflectionTwo, self).__init__(*args, **kwargs)
self.fields['adjective'].choices = kwargs.get("choices")