Handle data between forms

2019-02-17 23:33发布

If I have a registration with 3 steps, that will use 3 forms.

Something like this, just to demonstrate:

@app.route('/form/step1', methods=['GET', 'POST'])
def form_step1():
    form = form_step_1(request.form)
    ...validate()...
    return render_template('register.html', form=form)

@app.route('/form/step2', methods=['GET', 'POST'])
def form_step2():
    form = form_step_2(request.form)
    ...validate()...
    return render_template('register.html', form=form)

@app.route('/form/step3', methods=['GET', 'POST'])
def form_step3(): 
    form = form_step_3(request.form)
    ...validate()...
    return render_template('register.html', form=form)

What is the correct way to handle data between these three steps? All data should be committed to database at the end of the step 3. But a back action between the forms should populate again the previous form.

Use sessions for this purpose seems bad.

2条回答
▲ chillily
2楼-- · 2019-02-18 00:35

If you do not have any reason to worry about POST hijacking of your form data, you can use hidden form fields in the 2nd and 3rd view to pass data along. Think along these lines...

forms.py
# override EACH form's init to change the widget for each field to a hidden widget if is_hidden kwarg passed. 

class form_step_1(forms.Form):

    def __init__(self, *args, **kwargs):
        is_hidden = kwargs.pop('is_hidden', None)
        super(FormName, self).__init__(*args, **kwargs)
        if is_hidden:
            for field in self.fields:
                self.fields[field].widget = forms.HiddenInput()

# Be sure to do this for each form with hidden input needed


views.py

@app.route('/form/step1', methods=['GET', 'POST'])
def form_step1():
    form1 = form_step_1(request.POST)
    ...validate()...
    return render_template('register.html', {'form':form1})  

@app.route('/form/step2', methods=['GET', 'POST'])
def form_step2():
    form1 = form_step_1(request.POST, is_hidden=True)
    hidden_forms =[form1]
    form2 = form_step_2(request.POST)
    ...validate()...
    return render_template('register.html', {'form':form2, 'hidden_forms':hidden_forms})

@app.route('/form/step3', methods=['GET', 'POST'])
def form_step3(): 
    form1 = form_step_1(request.POST, is_hidden=True)
    form2 = form_step_2(request.POST, is_hidden=True)
    hidden_forms =[form1, form2]
    form = form_step_3(request.form)
    ...validate()...
    if form.is_valid():
        # do stuff, save to DB
        form1.save()
        form2.save()
        form3.save()
        return HttpReturnRedirect('/success_page/') # Always Redirect after posting form
    # if not valid, show again.
    return render_template('register.html', {'form':form, 'hidden_forms':hidden_forms })


template.html (assuming you are using a single template for each page

<form action="." etc>
    {% csrf_token %}
    {{ form }}
    {% for each_form in hidden_forms %}
        {{ each_form }}

    <!-- your submit button -->
</form>

Now, when your form goes to POST in step 3, if valid, each form's data from the previous steps are available.

If you want a foolhardy solution (that requires a bit more work), look into Django FormWizard

查看更多
霸刀☆藐视天下
3楼-- · 2019-02-18 00:36

I would personally suggest using the session object to pass data from one form to another. If you have a small amount of data then you can get away with just using the cookie implementation that flask has. Otherwise, you can override the default sessions object to store sessions data server side using Redis. This lets you use session objects without paying the price of storing lots of data in cookies. This means you can do something like

@app.route('/form/step1', methods=['GET', 'POST'])
def form_step1():
    form1 = form_step_1(request.POST)
    user_id = current_user.user_id # If you're using flask-login
    ...validate()...
        # dictionary that holds form1, form2, etch
        form_data = {"form1": form1, "form2": None, "Form3"=None} 
        flask.session[user_id] = form_data
        redirct_to(url_for("form_step2"))
    return render_template('register.html', {'form':form1})  

@app.route('/form/step2', methods=['GET', 'POST'])
def form_step2():
    form1 = session[user_id][form1]
    # A simpler way than passing the whole form is just the data 
    # you want but for this answer I'm just specifying the whole form.
    form = form_step_2(form1)
    user_id = current_user.user_id # If you're using flask-login 
    ...validate()...
        # dictionary that holds form1, form2, etch
        flask.session[user_id]["form2"] = form2
        redirct_to(url_for("form_step3"))
    return render_template('register.html', form=form)
查看更多
登录 后发表回答