form wizard initial data for edit not loading properly in Django?
Asked Answered
A

1

0

I have a three page form-list coming out of a single model. I could save the model first time, but when I want to edit the model, only the first form shows the initial value, subsequent forms does not show the initial data. but when I print the initial_dict from views, I can see all the initial views correctly. I followed this blog on form wizard.

Here is my model.py:

class Item(models.Model):
    user=models.ForeignKey(User)
    price=models.DecimalField(max_digits=8,decimal_places=2)
    image=models.ImageField(upload_to="assets/", blank=True)
    description=models.TextField(blank=True)

    def __unicode__(self):
        return '%s-%s' %(self.user.username, self.price)

urls.py:

urlpatterns = patterns('',

url(r'^create/$', MyWizard.as_view([FirstForm, SecondForm, ThirdForm]), name='wizards'),
url(r'^edit/(?P<id>\d+)/$', 'formwizard.views.edit_wizard', name='edit_wizard'),
)

forms.py:

class FirstForm(forms.Form):
    id = forms.IntegerField(widget=forms.HiddenInput, required=False)
    price = forms.DecimalField(max_digits=8, decimal_places=2)
    #add all the fields that you want to include in the form

class SecondForm(forms.Form):
    image = forms.ImageField(required=False)

class ThirdForm(forms.Form):
    description = forms.CharField(widget=forms.Textarea)

views.py:

class MyWizard(SessionWizardView):
    template_name = "wizard_form.html"
    file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT))
    #if you are uploading files you need to set FileSystemStorage
    def done(self, form_list, **kwargs):
        for form in form_list:
           print form.initial
        if not self.request.user.is_authenticated():
                raise Http404
        id = form_list[0].cleaned_data['id']
        try:
                item = Item.objects.get(pk=id)
                ######################   SAVING ITEM   #######################
                item.save()
                print item
                instance = item
        except:
                item = None
                instance = None
        if item and item.user != self.request.user:
                print "about to raise 404"
                raise Http404
        if not item:
                instance = Item()
                for form in form_list:
                    for field, value in form.cleaned_data.iteritems():
                        setattr(instance, field, value)
                instance.user = self.request.user
                instance.save()
            return render_to_response('wizard-done.html', {
                'form_data': [form.cleaned_data for form in form_list], })


    def edit_wizard(request, id):
        #get the object
        item = get_object_or_404(Item, pk=id)
        #make sure the item belongs to the user
        if item.user != request.user:
            raise HttpResponseForbidden()
        else:
            #get the initial data to include in the form
            initial = {'0': {'id': item.id,
                             'price': item.price,
                             #make sure you list every field from your form definition here to include it later in the initial_dict
            },
                       '1': {'image': item.image,
                       },
                       '2': {'description': item.description,
                       },
            }
            print initial
            form = MyWizard.as_view([FirstForm, SecondForm, ThirdForm], initial_dict=initial)
            return form(context=RequestContext(request), request=request)

template:

<html>
<body>
<h2>Contact Us</h2>
  <p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
  {% for field in form %}
    {{field.error}}
  {% endfor %}

  <form action={% url 'wizards' %} method="post" enctype="multipart/form-data">{% csrf_token %}
  <table>
  {{ wizard.management_form }}
  {% if wizard.form.forms %}
      {{ wizard.form.management_form }}
      {% for form in wizard.form.forms %}
          {{ form }}
      {% endfor %}
  {% else %}
      {{ wizard.form }}
  {% endif %}
  </table>
  {% if wizard.steps.prev %}
  <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">"first step"</button>
  <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">"prev step"</button>
  {% endif %}


  <input type="submit" value="Submit" />

  </form>

</body>
</html>

EDIT: one this I noticed is the following: On the edit mode, i.e, when I am at the following url : http://127.0.0.1:8000/wizard/edit/1/, it displays the first form data correctly, and when I click submit, it is not taking me to step-2 of edit mode, i.e the URL changes to http://127.0.0.1:8000/wizard/create/.

If upon clicking submit on edit url (like /wizard/edit/1) in the first step, same url is maintained then the form would get its initial data in next step. but I cannot figure out how to avoid the url from changing to /wizard/create

Assure answered 11/6, 2014 at 22:4 Comment(3)
One problem I see is you don't need to pass RequestContext() to wizard view call, so try removing that.Extramarital
@Extramarital I tried removing RequestContext() but that did not helpAssure
@Rohan: I added some more information in the edit. I am hoping you can help out. ThanksAssure
E
1

The error looks trivial. In your template the form action has wizards url, which is url of create view. Hence when the form is submitted it goes to /wizard/create.

To able to use the template for both views, you can remove the action attribute from form tag. The form will be submitted to current url which can be create or edit.

So change your template to have form tag as

<form method="post" enctype="multipart/form-data">

EDIT: To save item

Update your view as:

def done(self, form_list, **kwargs):
        for form in form_list:
           print form.initial
        if not self.request.user.is_authenticated():
                raise Http404
        id = form_list[0].cleaned_data['id']
        try:
                item = Item.objects.get(pk=id)
                print item
                instance = item
        except:
                item = None
                instance = None
        if item and item.user != self.request.user:
                print "about to raise 404"
                raise Http404
        if not item:
                instance = Item()

        #moved for out of if
        for form in form_list:
            for field, value in form.cleaned_data.iteritems():
                setattr(instance, field, value)
        instance.user = self.request.user
        instance.save()

        return render_to_response('wizard-done.html', {
                'form_data': [form.cleaned_data for form in form_list], })
Extramarital answered 14/6, 2014 at 10:7 Comment(4)
Thanks, that helped. but in the edit mode, the model is not overwritten. That is changes are not saved. can you help with what is wrong?Assure
@brainstorm, in your view, if you get item you don't actually use it to save.Extramarital
Thanks for commenting, I have inserted item.save() in the done method now (you can see in the code above). but that did not save the editing the model. Do you think I should save the item somewhere else?Assure
:worked like a charm. Thank you very much. you might be able to help me with this post: you already commented on this post. but I am seeing two copies of images being uploaded (one to fileStorage and one to upload path). how to avoid that? #24176568Assure

© 2022 - 2024 — McMap. All rights reserved.