Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flask-bootstrap with two forms in one page

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?

like image 795
nos Avatar asked Dec 08 '22 21:12

nos


1 Answers

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!

like image 166
Grey Li Avatar answered Dec 22 '22 00:12

Grey Li