We are building a wizard using Django 1.4's new form wizard functionality.
The docs on this are very terse and we can't find any advanced examples. We are using a named step wizard (needed to support a listview/datagrid we use) and a session backend.
The wizard is meant to edit roles and linked rights and is built to provide both add and edit functionality. We do this by asking the user in the first step if he/she wants to add or edit.
The next step depends on that choice; If the user wants to edit, there is a search screen, followed by a listview/datagrid that displays results. The user can then select one of the results and goes to a details-screen, followed by a FilteredSelectMultiple page, allowing him/her to link rights to this role.
If the user wants to add a new role, the search and results screens are skipped and the user goes directly to the details screen, followed by the link-screen.
It all works pretty well, using a condition_dict in urls.py, but we are wondering a couple of things about the general functionality:
When a specific pre-existing role is selected, how can we fill the details and the link-screen with the corresponding data?
Do we instantiate a roles-object and pass it somehow to the two forms, if so, where do we instantiate it and do we need to do that for every form separately (which seems a bit over the top)?
When saving, is it common practice to create another instance of a role object, add the form data to it and save, or can we re-use the object used in the forms somehow?
We have tried overloading get_form_instance to return instances of roles, and we have looked at instance_dict in the docs, but it feels like the wrong approach and there are no examples to be found online, and we're not even sure these are used to pre-fill data or even if we're on the right track.
Logically, I would say in the step that selects an existing role, I need to fill the wizard-variables using an instance of the chosen object, and these get displayed in the forms. At the end of the wizard we reverse the process and get all data from the wizard-variables and add them to a newly instantiated roles-object and save it. Ideally this instance will determine itself if it needs to perform an INSERT or an UPDATE, depending on whether or not the promary key is filled.
If anyone can provide an example, or a nudge in the right direction, it would be very much appreciated.
The code of the wizardview class in views.py is below:
class RolesWizard(NamedUrlSessionWizardView):
def get_template_names(self):
# get template for each step...
if self.steps.current == 'choice':
return 'clubassistant/wizard_neworeditrole.html'
if self.steps.current == 'search':
return 'clubassistant/wizard_searchrole.html'
if self.steps.current == 'results':
return 'clubassistant/wizard_pickrole.html'
if self.steps.current == 'details':
return 'clubassistant/wizard_detailsrole.html'
elif self.steps.current == 'rights':
return 'clubassistant/wizard_roles.html'
def get_context_data(self, form, **kwargs):
# get context data to be passed to the respective templates
context = super(RolesWizard, self).get_context_data(form=form, **kwargs)
# add the listview in the results screen
if self.steps.current == 'results':
# get search text from previous step
cleaned_data = self.get_cleaned_data_for_step('search')
table = RolesTable(Roles.objects.filter(
role_name__contains=cleaned_data['searchrole'])
)
RequestConfig(self.request, paginate={
"per_page": 4,
}).configure(table)
# add the listview with results
context.update({'table': table})
# add a role instance based on the chosen primary key
if self.steps.current == 'rights':
cleaned_data = self.get_cleaned_data_for_step('results')
role_id = cleaned_data['role_uuid']
role = get_object_or_404(Roles, pk=role_id)
context.update({'role': role})
return context
def done(self, form_list, **kwargs):
# this code is executed when the wizard needs to be completed
# combine all forms into a single dictionary
wizard = self.get_all_cleaned_data()
if wizard.get("neworeditrole")=="add":
role = Roles()
else:
role = get_object_or_404(Roles, pk=wizard.get("role_uuid"))
# many-to-many rights/roles
role.role_rights_new_style.clear()
for each_right in wizard.get('role_rights_new_style'):
RightsRoles.objects.create(role=role, right=each_right,)
# other properties
for field, value in self.get_cleaned_data_for_step('details'):
setattr(role, field, value)
role.save()
# return to first page of wizard...
return HttpResponseRedirect('/login/maintenance/roles/wizard/choice/')