开发者

How to pass previous form data to the constructor of a DynamicForm in FormWizard

I have a FormWizard where I need data from the first form to pass to the constructor of the second form so I can build a dynamic form.

I can get the first form's data via the process_step of the FormWizard.

I create the fields of the second form with a database call of the list of fields.

class ConditionWizardDynamicQuestions(forms.Form):

    def __init__(self, DynamicQuestions=None, *args, **kwargs):
       super(ConditionWizardDynamicQuestions, self).__init__(*args, **kwargs)
       开发者_Go百科questions = Question.objects.filter(MYDATA = DATA_FROM_1STFORM)
       for q in questions:
            dynField = FieldFactory(q)
            self.fields[q.label] = dynField

How can I pass over the DATA_FROM_1STFORM ?


my resultant code: I abandoned the init of the form, and switched it to the CreateQuestions def. Then used the wizard's get_form override to alter the form after creation.

class ConditionWizard(SessionFormWizard):
    def get_form(self, request, storage, step=None, data=None, files=None):
        form = super(ConditionWizard, self).get_form(request, storage, step, data, files)
        stepIndex = self.get_step_index(request, storage, step)
        if stepIndex == 1:
            form.CreateQuestions(request.session["WizardConditionId"])
        if stepIndex == 3:
            form.fields['hiddenConditionId'].initial = request.session["WizardConditionId"]
            form.fields['medicationName'].queryset = Medication.objects.filter(condition = request.session["WizardConditionId"])
        return form


I solved this by overriding get_form_kwargs for the WizardView. It normally just returns an empty dictionary that get_form populates, so by overriding it to return a dictionary with the data you need prepopulated, you can pass kwargs to your form init.

def get_form_kwargs(self, step=None):
    kwargs = {}
    if step == '1':
        your_data = self.get_cleaned_data_for_step('0')['your_data']
        kwargs.update({'your_data': your_data,})
    return kwargs

Then, in your form init method you can just pop the kwarg off before calling super:

self.your_data = kwargs.pop('client', None)


FormWizard already passes the data from each previous form to the next form. If you want to get that data in order to instantiate a class (for example, if a form has special keyword arguments that it requires), one way of doing it is to grab the querydict by overriding get_form in your form wizard class. For example:

class SomeFormWizard(FormWizard):
    def get_form(self, step, data=None):
        if step == 1 and data: # change this to whatever step requires
                               # the extra data
            extra_data = data.get('key_from_querydict')
            if extra_data:
                return self.form_list[step](data,
                                            keyword_argument=extra_data,
                                            prefix=self.prefix_for_step(step),                                                                                                                                            
                                            initial=self.initial.get(step, None))
        # Fallback for the other forms.
        return self.form_list[step](data,
                                    prefix=self.prefix_for_step(step),                                                                                                                                            
                                    initial=self.initial.get(step, None))

Note that you can also override parse_params(self, request, *args, **kwargs) in FormWizard to access the url/request data, just like you would in a view, so if you have request data (request.user, for instance) that is going to be needed for all of the forms, it might be better to get the data from there.

Hope this helps.


Override the get_form_kwargs method of your form wizard in views

view.py

class FormWizard(SessionWizardView):
    def get_form_kwargs(self, step=None):
        kwargs = {}
        if step == '1':
            step0_form_field = self.get_cleaned_data_for_step('0')['previous_form_field_data']
            kwargs.update({'step0_form_field': step0_form_field})
        return kwargs 

Override the init of your form by popping up the data you got from the previous field to create a dynamic field.

forms.py

class MyForm(forms.Form):
    #some fields

class MyForm1(forms.Form):
    def __init__(self, *args, **kwargs):
        extra = kwargs.pop('step0_form_field')
        super(MyForm1, self).__init__(*args, **kwargs)

        for i in range(extra):
            self.fields['name_%s' % i] = forms.CharField()


I was recently working with django form wizard, and i was solving the similar issue. I don't think you can pass data to init, however, what you can do, is override the init constructor:

next_form = self.form_list[1]

# let's change the __init__
# function which will set the choices :P
def __init__(self, *args, **kw):
    super(next_form, self).__init__(*args, **kw)
    self.fields['availability'].choices = ...
next_form.__init__ = __init__

It's quite annoying that in python you can't declare and assign a function in one go and have to put it in the namespace (unless you use lambdas), but oh well.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜