flask-bootstrap with two forms in one page
Asked Answered
A

1

4

I plan to put two forms in one page in my flask app, one to edit general user information and the other to reset password. The template looks like this

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block page_content %}                                                   
<div class="page-header">                                                  
    <h1>Edit Profile</h1>
</div>

{{ wtf.quick_form(form_profile, form_type='horizontal') }}                 

<hr>

{{ wtf.quick_form(form_reset, form_type='horizontal') }}                   

<hr>
{% endblock %} 

Each form has a submit button.

In the route function, I tried to separate the two form like this

form_profile = ProfileForm()
form_reset = ResetForm()

if form_profile.validate_on_submit() and form_profile.submit.data:
    ....
if form_reset.validate_on_submit() and form_reset.submit.data:
    .....

But it didn't work. When I click on the button in the ResetForm, the ProfileForm validation logic is executed.

I suspect the problem is that wtf.quick_form() creates two identical submit buttons, but not sure.

What should I do in this case? Can bootstrap/wtf.html template deal with this situation?

Article answered 28/9, 2016 at 4:29 Comment(0)
L
14

Define this two SubmitField with different names, like this:

class Form1(Form):
    name = StringField('name')
    submit1 = SubmitField('submit')

class Form2(Form):
    name = StringField('name')
    submit2 = SubmitField('submit')

Then in view.py:

....
form1 = Form1()
form2 = Form2()

if form1.submit1.data and form1.validate_on_submit():  # notice the order 
....
if form2.submit2.data and form2.validate_on_submit():  # notice the order 
....

Now the problem was solved.

If you want to dive into it, then continue read.

Here is validate_on_submit():

    def validate_on_submit(self):
        """
        Checks if form has been submitted and if so runs validate. This is
        a shortcut, equivalent to ``form.is_submitted() and form.validate()``
        """
        return self.is_submitted() and self.validate()

And here is is_submitted():

    def is_submitted(self):
        """
        Checks if form has been submitted. The default case is if the HTTP
        method is **PUT** or **POST**.
        """

        return request and request.method in ("PUT", "POST")

When you call form.validate_on_submit(), it check if form has been submitted by the HTTP method no matter which submit button was clicked. So the little trick above is just add a filter (to check if submit has data, i.e., form1.submit1.data).

Besides, we change the order of if, so when we click one submit, it only call validate() to this form, preventing the validation error for both form.

The story isn't over yet. Here is .data:

@property
def data(self):
    return dict((name, f.data) for name, f in iteritems(self._fields))

It return a dict with field name(key) and field data(value), however, our two form submit button has same name submit(key)!

When we click the first submit button(in form1), the call from form1.submit1.data return a dict like this:

temp = {'submit': True}

There is no doubt when we call if form1.submit.data:, it return True.

When we click the second submit button(in form2), the call to .data in if form1.submit.data: add a key-value in dict first, then the call from if form2.submit.data: add another key-value, in the end, the dict will like this:

temp = {'submit': False, 'submit': True}

Now we call if form1.submit.data:, it return True, even if the submit button we clicked was in form2.

That's why we need to define this two SubmitField with different names. By the way, thanks for reading(to here)!

Thanks for nos's notice, he add an issue about validate(), check the comments below!

Lubalubba answered 28/9, 2016 at 6:43 Comment(3)
Thanks for the tip. I also noticed that and form1.submit1.data is not needed anymore.Article
You are welcome, but I though the and form1.submit1.data is needed, the second filter form2.submit2.data can be removed instead. I update the answer.Lubalubba
Thanks again for the extended explanation. I noticed there is one more issue: When the validation of the first form fails, the error message from both forms show up, which is confusing for the users since they might think both forms need to be filled. The problem is that the form validation is always called for the 2nd form. It can be resolved by changing the logics in if, i.e., if form2.submit2.data and form2.validate_on_submit() :. Then if the form not submitted, it is not validated.Article

© 2022 - 2024 — McMap. All rights reserved.