0

I have a form wizard in Django. In the first step I need to insert data about a transport request, and a message, which is in another model. In second step I add passengers using a formset, and in the last one, the addresses, also in a formset.

I don't know how to insert the message field in the first step. I tried to add a formset in the context overriding the get_context_data method from the SessionWizardView (code below). It shows the field, but I couldn't figure it out how to save it.

I saw this post Django form with fields from two different models, but the SessionWizardView does not allow me to add a view with two forms in a step, I can only add forms.

I have also read this one django wizard, using form and formset in the same step, but it doesn't works, maybe because it is from 6 years ago.

My last try was to add a custom field in the transport ModelForm (not in the code below), but I think it will not be the right way to do it (I still don't know how to save the message this way).

The code is like this:

models.py

class Transporte(models.Model):
    #fields about transport here

class Mensagem(models.Model):
    #this is the message field
    mensagem = models.TextField("Mensagem", blank=True)
    transporte = models.ForeignKey(Transporte, on_delete=models.CASCADE)

...

forms.py

class FormTransporte(forms.ModelForm):
    class Meta:
        model = Transporte

class FormMensagem(forms.ModelForm):
    class Meta:
        model = Mensagem
        exclude = ()

FormsetMensagem = inlineformset_factory(Transporte, Mensagem, form=FormMensagem,extra=1)
...

views.py

FORMS = [("base", FormTransporte),
         ("passageiros",FormsetPassageiros),
         ("enderecos",FormsetEnderecos)
         ]

TEMPLATES = {"base":'transporte/solicitar.html',
             "passageiros":'transporte/passageiros.html',
             "enderecos":'transporte/enderecos.html'}

class WizardView(SessionWizardView):
    def get_template_names(self):
        return [TEMPLATES[self.steps.current]]

    def get_context_data(self,form, **kwargs):
    #Here I try to append the formset in the first step. It shows the
    #field, but no success on saving it
        data = super(WizardView,self).get_context_data(form,**kwargs)
        if self.steps.current == "base":
            if self.request.POST:
                data['mensagem'] = FormsetMensagem(self.request.POST)
            else:
                data['mensagem'] = FormsetMensagem()
        return data;

    def done(self,form_list,form_dict,**kwargs):
        form_transporte = form_dict['base']
    #the line below doesn't works
        form_mensagem = form_dict['base']['mensagem']
        instance_transporte = form_transporte.save()

        if 'enderecos' in form_dict:
            formset_endereco = form_dict['enderecos']
            for form_endereco in formset_endereco:
                endereco = form_endereco.save(commit=False)
                endereco.transporte = instance_transporte
                endereco.save()

        if 'passageiros' in form_dict:
            formset_passageiros = form_dict['passageiros']
            for form_passageiros in formset_passageiros:
                passageiros = form_passageiros.save(commit=False)
                passageiros.transporte = instance_transporte
                passageiros.save()

        return render(self.request,'transporte/done.html',{
            'form_data': [form.cleaned_data for form in form_list],
        })

...

urls.py

app_name = 'transporte'

urlpatterns = [
    ...
    path(r'solicitar/',login_required(views.WizardView.as_view(views.FORMS, condition_dict={'passageiros': views.tem_passageiros})), name='solicitar'),
    ...
]

Any ideas?

William
  • 331
  • 1
  • 3
  • 12
  • 1
    what you can do is define the `message_form` as a property on your `transport_form` in its `__init__()` method. Then in its `clean()` method, also clean the message_form and raise a validation error if it's not valid. Finally in the `save()` method also save the message form. – dirkgroten May 02 '19 at 15:41
  • Don't know if I have done it right, but when I included the message_form property in the first step, the message field was loaded correctly, but after the last step of the wizard, the form was gone, and the message field had appeared as part of the first form, not a separated form. It worked, but I had to assign the message field to the message of the form, transport instance, and then save the message. I think that, for this case, it is easier to create a custom field in the first form and, in the done message, create a new instance of the message model and save it directly. – William May 03 '19 at 14:42

0 Answers0