I have a Django form wizard working nicely for creating content of one of my models. I want to use the same Wizard for editing data of existing content but can\'t find a good ex
An update that works with Django 3.1 and django-formtools 2.2. In my case i had two models (Colaborattor and User), connected through OneToOneField. I show the user form in step 0 and colaborattor form in step 1:
models.py:
class Colaborattor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
nome = models.CharField("Nome completo", max_length=128)
...
views.py:
class UpdateColaborattorWizard(SessionWizardView):
form_list = [forms.UserRegisterForm, forms.ColaborattorForm]
template_name = "criar_colaborador.html"
def get_form_instance(self, step):
# step 0: user form
if 'colaborattor_pk' in self.kwargs and step == '0':
colaborattor_pk = self.kwargs['colaborattor_pk']
colaborattor = models.Colaborattor.objects.get(pk=colaborattor_pk)
user = colaborattor.user
return user
# step 1: colaborattor form
elif 'colaborattor_pk' in self.kwargs and step == '1':
colaborattor_pk = self.kwargs['colaborattor_pk']
colaborattor = models.Colaborattor.objects.get(pk=colaborattor_pk)
return colaborattor
# The default implementation
return self.instance_dict.get(step, None)
I hope it helps someone with the same problem.
I had to edit this a bit to get it working in Django 1.11 with django-formtools 2.1.
class ProjectWizard(SessionWizardView):
def get_form_initial(self, step):
if 'project_id' in self.kwargs:
return {}
return self.initial_dict.get(step, {})
def get_form_instance(self, step):
if not self.instance_dict:
if 'project_id' in self.kwargs:
project_id = self.kwargs['project_id']
return Project.objects.get(id=project_id)
return None
The get_form_instance
method now expects either an object or None to be returned.
I've just got this working so will post the answer in case it helps someone else.
You can pass the ID of the item you'd like to edit in urls.py like this:
(r'^projects/edit/(?P<project_id>[-\d]+)$', ProjectWizard.as_view(FORMS)),
You can then look up the item with following code in
views.py:
class ProjectWizard(SessionWizardView):
def get_form_initial(self, step):
if 'project_id' in self.kwargs and step == 'project_essentials':
project_id = self.kwargs['project_id']
project = Project.objects.get(id=project_id)
from django.forms.models import model_to_dict
project_dict = model_to_dict(project)
return project_dict
else:
return self.initial_dict.get(step, {})
You need to convert the model to a dict so you can set it as the initial data.
pxg's answer is insufficient. As pointed out by emin-buğra-saral it creates a new instance of the model rather than editing it. And emin-buğra-saral's answer, by itself, isn't enough. Either don't override the get_form_initial method and don't set an initial_dict value or use the implementation provided in this answer. This is how you should combine their answers:
in urls.py:
(r'^projects/edit/(?P<project_id>[-\d]+)$', ProjectWizard.as_view(FORMS)),
in views.py:
class ProjectWizard(SessionWizardView):
def get_form_initial(self, step):
if 'project_id' in self.kwargs:
return {}
return self.initial_dict.get(step, {})
def get_form_instance(self, step):
if not self.instance:
if 'project_id' in self.kwargs:
project_id = self.kwargs['project_id']
self.instance = Project.objects.get(id=project_id)
else:
self.instance = Project()
return self.instance
While pxg's version of get_form_initial would actually work (as long as you also add the get_form_instance override) it's not necessary to look up the instance, extract its data, and create an initial value dictionary. All this is done automatically by the ModelForm prior to initializing the instance from initial_dict. By simply returning an empty initial value dictionary you'll have simpler, more efficient code.
Addition to pxg's answer, get_form_instance should be like this, otherwise you won't be editing the model but create a new instance of it:
def get_form_instance(self, step):
if not self.instance:
if 'initial_id' in self.kwargs:
initial_id = self.kwargs['initial_id']
self.instance = Project.objects.get(id=initial_id)
else:
self.instance = Project()
return self.instance