Skip to content

Allow dynamic form classes with WizardView #16

@gchp

Description

@gchp

This issue was originally submitted on Trac (#21667). I've added some snippets from the original ticket below, see the link above for the full conversation.


The WizardView does not currently support dynamic form classes without overriding the entire get_form method.

My mixin below demonstrates an easy way to make this convenient. I needed this functionality to support using modelform_factory at runtime to accommodate logic that varies depending on choices made previously in the wizard. A simple use case is to support dynamic "required" fields.

class WizardDynamicFormClassMixin(object):
    def get_form_class(self, step):
        return self.form_list[step]

    def get_form(self, step=None, data=None, files=None):
        """
        This method was copied from the base Django 1.6 wizard class in order to
        support a callable `get_form_class` method which allows dynamic modelforms.

        Constructs the form for a given `step`. If no `step` is defined, the
        current step will be determined automatically.

        The form will be initialized using the `data` argument to prefill the
        new form. If needed, instance or queryset (for `ModelForm` or
        `ModelFormSet`) will be added too.
        """
        if step is None:
            step = self.steps.current
        # prepare the kwargs for the form instance.
        kwargs = self.get_form_kwargs(step)
        kwargs.update({
            'data': data,
            'files': files,
            'prefix': self.get_form_prefix(step, self.form_list[step]),
            'initial': self.get_form_initial(step),
        })
        if issubclass(self.form_list[step], forms.ModelForm):
            # If the form is based on ModelForm, add instance if available
            # and not previously set.
            kwargs.setdefault('instance', self.get_form_instance(step))
        elif issubclass(self.form_list[step], forms.models.BaseModelFormSet):
            # If the form is based on ModelFormSet, add queryset if available
            # and not previous set.
            kwargs.setdefault('queryset', self.get_form_instance(step))
        return self.get_form_class(step)(**kwargs)

This is a simple demonstration of usage:

def get_form_class(self, step):
    if step == STEP_FOO:
        try:
            choice_foo = self.get_cleaned_data_for_step(STEP_FOO)["choice_foo"]
        except KeyError:
            pass
        else:
            # get_wizard_form_class() would return a model form with fields
            # that depend on the value of "choice"
            return ModelFoo(choice=choice_foo).get_wizard_form_class()
    return super(WizardFoo, self).get_form_class(step)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions