开发者

can you use Form Wizard for Model Forms in django?

开发者 https://www.devze.com 2023-03-20 08:50 出处:网络
I have one model and I\'ve created a form out of the model using ModelForm.Now, I want to spread the form across two pages.For example, the first three fields will appear on the first page then the us

I have one model and I've created a form out of the model using ModelForm. Now, I want to spread the form across two pages. For example, the first three fields will appear on the first page then the user clicks next and the last three fields appear on the second page. Then he clicks submit and the user submitted data is added to the database.

I took a look at the docs for the Form Wizard and it seems like it would work for model forms as well? Can someone confirm this?

And if it does, can someone explain the process of creating a WizardView class.

This example is given in the docs and I don't understand what the second two parameters are. Is form_list just a list of form objects that you've instantiated based on your form classes? And what is **kwargs?

class ContactWizard(SessionWizardView):
    def done(self, form_list, **kwargs):
        do_something_with_the_form_data(form_list)
开发者_运维问答        return HttpResponseRedirect('/page-to-redirect-to-when-done/')

Thanks in advance for your help!


Say your model has two fields

class AModel( Model ):
    fieldA = CharField()
    fieldB = CharField()

We want to set each field in a separate step using a FormWizard. So we create two ModelForms, each showing one field:

class Form1( ModelForm ):
    class Meta:
        model = AModel
        fields = ( 'fieldA', )

class Form2( ModelForm ):
    class Meta:
        model = AModel
        fields = ( 'fieldB', )

We call our form wizard AWizard; the url.py entry should look something like

url( r'^$', AWizard.as_view( [ Form1, Form2 ] ) ),

In the implementation of AWizard we need to make sure all the forms write their data to a single instance, which we then save to the database:

class AWizard( SessionWizardView ):
    instance = None

    def get_form_instance( self, step ):
        if self.instance is None:
            self.instance = AModel()
        return self.instance

    def done( self, form_list, **kwargs ):
        self.instance.save()

Notice that we override the method get_form_instance. This method returns the model instance the forms bind to.

You might think (I did), that this method creates an instance for the first request (the first step of the wizard), and then keeps using that same instance for all steps.

Actually, it's a little more complicated. For each request a new instance of AWizard is created, which in turn creates a new AModel instance. So, the steps don't share a single instance to start with.

The magic happens when the last form is submitted. At this point all forms are revalidated, each form calls get_form_instance and they end up populating a single AModel instance.

That instance is then saved in done.


Form Wizard is being built into Django 1.4 so is a good way to go about this. It should do what you want, but you may need a couple of tweaks.

Don't worry about the kwargs in done() at the moment - you're not going to need them.

form_list is the list of forms that you want to use for your steps - from urls.py

urlpatterns = patterns('',
    (r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
)

[ContactForm1, ContactForm2] will be passed to done() as form_list.

What you will need to do is break your ModelForm into separate forms. The easiest way to do this (if you want your model on several forms) is to not use ModelForm but just create your own form. It's pretty easy:

from django import forms

class ContactForm1(forms.Form):
    subject = forms.CharField(max_length=100)
    sender = forms.EmailField()

class ContactForm2(forms.Form):
    message = forms.CharField(widget=forms.Textarea)

Once your forms reflect the portions of your model, just create the views and patterns as described in the docs and set do_something_with_the_form_data(form_list) to a function that completes your model from the form data and then does a save.

You could use ModelForm but - only if you can persuade it to produce different forms for Form Wizard to use for each step - that's going to be the tricky part.


The view proposed by @wuerg did not work for me, I had to do this:

class AWizard( SessionWizardView ):
    def dispatch(self, request, *args, **kwargs):
        self.instance = AModel()
        return super(ApplyWizard, self).dispatch(request, *args, **kwargs)

    def get_form_instance( self, step ):
        return self.instance

    def done( self, form_list, **kwargs ):
        self.instance.save()
        return HttpResponseRedirect(reverse(thanks))


I had to alter the solution of @wuerg and @madmen to work in my usecase (saving the Model after every step). The big advantage of this approach is that it always uses the same instance of the AModel instead of creating a new instance for every step:

class AWizard(SessionWizardView):
    instance = AModel()

    def dispatch(self, request, *args, **kwargs):
        return super(AWizard, self).dispatch(request, *args, **kwargs)

    def get_form_instance(self, step):
        return self.instance

    def done(self, form_list, **kwargs):
        self.save_model()
        return render_to_response('done.html')
0

精彩评论

暂无评论...
验证码 换一张
取 消