Web application: Flask - html - wtforms: form with two submit buttons - python

I am new to programming. I am interested in developing a simple web app with two input values and two submit buttons. Numbers A and B are input by users and the product OR the sum will be shown depending on which button is pressed. When I use one button, the code works; however, I can't seem to figure out how to make both buttons work. I wrote the application in Flask and Python 3.8 and used wtforms.
Here are my codes:
view.html
<table>
{% for field in form %}
<tr>
<td>{{ field.name }}</td><td>{{ field }}</td>
<td>{{ field.label }}</td>
</tr>
{% endfor %}
</table>
<form method="post" action="">
<input type="submit" name="btn" value="Add">
</form>
<p>
<form method="post" action="">
<input type="submit" name="btn" value="Multiply">
</form></p>
<p>
{% if result != None %}
{{result}}
{% endif %}
</p>
Python code
views.py
#app.route("/comp", methods=['GET', 'POST'])
def comp():
form = InputForm(request.form)
if request.method == 'POST' and form.validate():
if request.form["btn"] == "Add":
result = add(form.A.data, form.B.data)
elif request.form["btn"] == "Multiply":
result = mul(form.A.data, form.B.data)
else: result = None
return render_template('view.html', form=form, result=result)
I defined mul() and add() functions in a separate file, as I plan to expand the application further in the future. Here is the function definition:
def mul(A,B):
return A*B
def add(A,B):
return A+B
My input.py file:
from wtforms import Form, FloatField, validators
class InputForm(Form):
A = FloatField(
label='A', default=0,
validators=[validators.InputRequired()])
B = FloatField(
label='B', default=0,
validators=[validators.InputRequired()])

One of your values is "ADD", but you test for "Add". So, the equality test fails
UPDATE:
Where are you defining add() and mul()? Perhaps instead use:
if request.form['btn'] == 'Add':
result = form.A.data + form.B.data
elif request.form['btn'] == 'Multiply':
result = form.A.data * form.B.data
UPDATE2:
Try this to diagnose:
if request.method == 'POST' and form.validate():
print(request.form["btn"])
print(form.A.data, form.B.data)
print(request.POST)

You have 3 different forms. A and B are not part of either form you are submitting.

OK so I troubleshot your code. First thing first this is the result:
Flask Code :
from flask import Flask, request, render_template, url_for, flash, redirect
from wtforms import SubmitField, Form, FloatField, validators
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dev'
def mul(A,B):
return A*B
def add(A,B):
return A+B
# Form
class InputForm(Form):
A = FloatField(
label='A', default=0,
validators=[validators.InputRequired()])
B = FloatField(
label='B', default=0,
validators=[validators.InputRequired()])
#app.route('/comp', methods=['GET', 'POST'])
#app.route('/', methods=['GET', 'POST']) #this so that both url will work
def comp():
result = '' #already assign a variable, else will throw error
form = InputForm(request.form)
if request.method == 'POST': #First we check if method is post **POINT 2**
if form.validate(): #Then if form is validate
print('form ok') #Test if form is ok
if request.form['btn'] == 'Add':
result = add(form.A.data, form.B.data)
print(result)
elif request.form['btn'] == 'Multiply':
result = mul(form.A.data, form.B.data)
print(result)
flash(result) # **POINT 3**
return redirect('comp') # This will make your code run forever #**4**
else:
print('form no ok ') #check if form no ok
else:
print('request is get') # if you put the form.validate with the method condition, the form will return False as soons as you render the template
return render_template('view.html', form=form, result=result)
if __name__ == '__main__':
app.run(debug=True)
HTML file:
<form method="post" action="">
<table>
<!-- Generates form fields -->
<!-- POINT 1 -->
{% for field in form %}
<td>{{ field.name }}</td><td>{{ field }}</td>
<td>{{ field.label }}</td>
</tr>
{% endfor %}
<tr>
<!-- Input are inside the form tag -->
<td><input type="submit" name="btn" value="Add"></td>
<td><input type="submit" name="btn" value="Multiply"></td>
</tr>
</table>
</form>
<!-- Print result in the browser -->
<!-- Needs to redirect to the same page -->
<!-- return redirect('comp') -->
{% for msg in get_flashed_messages()%}
<h1> {{msg}} </h1>
{% endfor%}
So the main issue was that in the HTML the form is not together with the input field, hence it was elaborating the form separately. By adding all together then it will start to work.
In the python file, the if request.method == 'POST' condition run simultaneously with the form hence returning the form False even before submit it.
.3 Added a Flash method so that you can see the result
.4 Add a redirect, this will flash the result to the browser, but most importantly will refresh the form and gives you possibility to add a new one.
Note that is also important to redirect the page because imagine if you want to commit it into a database, this will make you submit the form and save the changes.

If I understand the issue then try to do this:
from flask import render_template, redirect, url_for, flash, request
#app.route('/comp') #sample only
def comp():
form = InputForm(request.form)
if request.method == 'POST' and form.validate()
if request.form['btn'] == 'Add':
result = add(form.A.data, form.B.data)
elif request.form['btn'] == 'Multiply':
result = mul(form.A.data, form.B.data)
flash(result) # see the result in the browser
print('result') # see the result in the terminal
return redirect(url_for('comp'))
else:
result = None
return render_template('view.html', form=form, result=result)
to implement the flash in the html simple add this somewhere
{% for msg in get_flashed_messages()%}
<h1> {{msg}} </h1>
{% endfor %}
I always add a print statement and a flash for troubleshooting purposes in order to see where the issue is.

Related

[DJANGO]: How to pass a Django form field value to a template form action?

I have a Django form that asks for id number. Hence, when the user clicks on submit, that id number is passed as a parameter to the endpoint.
This URL path('verify/nin/', views.post_nin, name='post_nin') contains the form and asks for the id number while I am submitting the data to this URL to
path('nin/<str:nin>',
views.nin_verification_vw, name="nin_verification")
So I expect to be redirected to http://127.0.0.1:8000/api/nin/15374020766 but instead it is redirecting me to http://127.0.0.1:8000/api/nin/%3Cinput%20type=%22text%22%20name=%22nin%22%20required%20id=%22id_nin%22%3E?nin=15374020766&csrfmiddlewaretoken=u5UmwDW4KRUIvYWXAa64J8g1dTPoJ3yDqtoCuKjboIE2TNxI3tPbjPmCK6FztVwW
How do I avoid the unnecessary parameters?
Here is my forms.py:
class NINPostForm(forms.Form):
"""Form for a user to verify NIN"""
nin = forms.CharField(required=True, help_text='e.g. 123xxxxxxxx')
# check if the nin is a valid one
def clean_nin(self):
nin = self.cleaned_data['nin']
regex = re.compile("^[0-9]{11}$")
if not regex.match(nin):
raise forms.ValidationError("NIN is incorrect.")
return nin
Here is my views.py:
def post_nin(request):
submitted = False
if request.method == 'POST':
form = NINPostForm(request.POST)
if form.is_valid():
cd = form.cleaned_data['nin']
return HttpResponseRedirect('/verify/nin?submitted=True')
else:
form = NINPostForm()
context = {
'form': form,
# 'cd': cd,
}
return render(request, 'ninform.html', context)
And here is my HTML template:
<form action="{% url 'nin_verification' form.nin %}" method="POST">
<table>
{{ form.as_table }}
<tr>
<td><input type="submit" value="Submit"></td>
</tr>
</table>
{% csrf_token %}
</form>
first import redirect : from django.shortcuts import redirect
Change your view to :
<form action="{% url 'post_nin' %}" method="POST">
<table>
{{ form.as_table }}
<tr>
<td><input type="submit" value="Submit"></td>
</tr>
</table>
{% csrf_token %}
</form>
You were passing the whole field instead of a String by using form.nin in your form action you should use your post_nin view to parse the nin field so...
Change your view to :
def post_nin(request):
submitted = False # Don't understand this part
if request.method == 'POST':
form = NINPostForm(request.POST)
if form.is_valid():
nin = form.cleaned_data['nin']
return redirect('nin_verification', nin=nin)
else:
form = NINPostForm()
context = {
'form': form,
}
return render(request, 'ninform.html', context)
`

Multiple forms in a single page using flask and WTForms in which one form link to another

This is what it looks like now:
This is what I need
I need two forms one linking to another in a same page.
On the first form: there will be an entry box for the user to fill out.
If the entry is valid the system should lead the user to second form.
The form would print out the result the system find and ask the user to enter a number into the entry box.
The system will see if the entry is valid. If it is it will do a seires of action()
(words in bold are the part that works)
My code:
main function
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
if form1.submit1.data and form1.validate():
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
if form2.submit2.data and form2.validate(): #######
user_select = int(form2.area.data)
if(user_select>0 or user_select<=len(allMatch)):
user= allMatch[user_select-1]
author_name = user.username
posts = Post.query.filter_by(author=author_name).all()
emty_list = []
while(len(posts)!= emty_list):
db.session.delete(posts[0])
db.session.delete(user)
db.session.commit()
return redirect(url_for('home'))
flash('change have been made successfully', 'success')
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
else:
return redirect(url_for('searchArea'))
flash('check your entry', 'danger')#######
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
return render_template('searchArea.html', title='Account',form=form1)
forms.py
class FindArea(FlaskForm):
area1=TextAreaField('Area Code', validators=[DataRequired()])
submit1 = SubmitField('Search')
class SelectUser(FlaskForm):
area2=TextAreaField('user number', validators=[DataRequired()])
submit2 = SubmitField('confirm')
template(searchArea.html)
{% extends "layout.html"%} {% block content %}
<div>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.area1.label(class="form-control-label") }} {% if form.area1.errors %} {{ form.area1(class="form-control form-control-lg
is-invalid") }}
<div class="invalid-feedback">
{% for error in form.area1.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.area1(class="form-control form-control-lg") }}
{% endif %}
</div>
{{ form.submit1(class="btn btn-outline-info") }}
</form>
</div>
{% endblock content %}
template(print_area.html)
{% extends "layout.html"%} {% block content %}
<div>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.area.label(class="form-control-label") }} {% if form.area.errors %} {{ form.area(class="form-control form-control-lg
is-invalid") }}
<div class="invalid-feedback">
{% for error in form.area.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.area(class="form-control form-control-lg") }}
{% endif %}
</div>
{{ form.submit(class="btn btn-outline-info") }}
</form>
</div>
{% endblock content %}
Any help would be awsome!
Thanks!
When you click the submit button of form2, this condition(if form1.submit1.data and form1.validate():) is False.Maybe you need to change the code logic.
Okay so this is what I learn from this lesson. form.validate() and return render is a very tricky thing when it came to coding it run from top to bottm twice, first time when you enter the page it does not validate but render, second time it velidate; you can really do nothing to it except just add an variable to check if first form has been submitted, and return render for first when irst form has not been submitted and else return render for second. Here are my fixes of my code:
main function
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
global allMatch
form1Passer = False
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
if form1.submit1.data and form1.validate() and form1Passer == False: # notice the order
form1Passer == True
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
if form2.submit2.data and form2.validate(): # notice the order
user_select = int(form2.area2.data)
if(user_select>0 and user_select<=len(allMatch)):
print("0")
user= allMatch[user_select-1]
print("1")
print("2")
posts = Post.query.filter_by(author=user).all()
print(type(posts))
while(True):
try:
db.session.delete(posts[0])
except:
break
db.session.delete(user)
db.session.commit()
return redirect(url_for('home'))
flash('change have been made successfully', 'success')
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
else:
return redirect(url_for('searchArea'))
flash('check your entry', 'danger')
if form1Passer == False:
return render_template('searchArea.html', title='Account',form=form1)
else:
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
no modification need to be done. If you are having the same problem with me wish this would help you!
Edits: the code above totally works (but it's a poor practice) but when query returns a list the object in it is not linked to the object itself!
It's kinda like the adress of the supermarket!
When you change the adress you pu down wouldn't take affect to the actual location of the supermarket.
And when the superMarket moves, the adress would not automatic update itself!
using the filter method and .first() could avoid any oissible future problem! Since .first() is a direct controller of the object itself
This following code would also works when you only assigned a value to an object (ex: post_target.prperty = value) while the above will not(# is the line I modified):
allMatch=[] #
user; #
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
global allMatch
global user
form1Passer = False
form2Passer = False
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
form3 = ConfirmForm()
if form1.submit1.data and form1.validate() and form1Passer == False: # notice the order
form1Passer == True
if(form1.area1.data == "0"):
flash('this is an illegal move', 'danger')
return redirect(url_for('searchArea'))
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
if form2.submit2.data and form2.validate() and form2Passer == False: # notice the order
user_select = int(form2.area2.data)
if(user_select>0 and user_select<=len(allMatch)):
user= allMatch[user_select-1]
return render_template('confirm.html', title='Account',user=user,form=form3)
else:
flash('check your entry', 'danger')
return redirect(url_for('searchArea'))
if form3.submit_cancel.data and form3.validate():
return redirect(url_for('searchArea'))
if form3.submit_confirm.data and form3.validate():
user_target = User.query.filter_by(email = user.email).first()#
while(True):
try:
post_target = Post.query.filter_by(author=user_target).first()#
db.session.delete(post_target)#
db.session.commit()
except:
break
db.session.delete(user_target)#
db.session.commit()
flash('change have been made successfully', 'success')
return redirect(url_for('home'))
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
if form1Passer == False:
return render_template('searchArea.html', title='Account',form=form1)
elif form2Passer == False:
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
else:
return render_template('confirm.html', title='Account',user=user,form=form3)
elif current_user.is_authenticated:
abort(403)
else:
return redirect(url_for('home'))

a flask web application that obtains two data from a user only accepts data in one field

I'm noob in Flask and Python.
It is my frstfsfs fsf fsfsfs
My goal is to create an web application.
There are 3 files in my application.
In the browswer there are 3 fields:
The sine2 of ------------ equals
The cossine2 of ------------ equals
sin2(x) + co2(x)------------ equals
Only the field sine2 works showing the value.
Nothing else works.
The goal is:
Enter a value in sine2 and click on the equal. The result of sine2 is showed.
The value appears in the field cosine2 and sin2+cos2. After the cliking on equal the cossine2 is showed.
Then click on the square sine to show the value.
The same applies when the cosine value is entered first.
visualizing in browser
controller.py:
from flask import Flask, render_template, request
from compute import *
from model import InputForm
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
form = InputForm(request.form)
if request.method == 'POST' and form.validate():
r = form.r.data
s = sin2(r)
else:
s = None
return render_template("view.html", form=form, s=s)
if request.method == 'POST' and form.validate():
d = form.r.data
e = cos2(d)
else:
e = None
return render_template("view.html", form=form, e=e)
if request.method == 'POST' and form.validate():
f = form.r.data
h = sin2cos2(f)
else:
h = None
return render_template("view.html", form=form, h=h)
if __name__ == '__main__':
app.run(debug=True)
compute.py
import math
def sin2(r):
return (math.sin(r)) ** 2
def cos2(r):
return (math.cos(r)) ** 2
def sin2cos2(r):
return (math.sin(r)) ** 2 + (math.cos(r)) ** 2
/template/view.html
<form method=post action="">
The sine2 of
{{ (form.r) }}
<input type=submit value=equals>
{% if s != None %}
{{ s }}
{% endif %}
<div>
The cossine2 of
{{ (form.r) }}
<input type=submit value=equals>
{% if e!= None %}
{{ e }}
{% endif %}
</div>
<div>
sin2(x)+cos2(x)
{{ (form.r) }}
<input type=submit value=equals>
{% if h!= None %}
{{ h }}
{% endif %}
</div>
</form>
After you return the function ends and any code written after is not ran. In your situation you have three return statements, so that is why only sine2 is working because everything after is never ran. Try removing the first two return statements and replacing the last return with:
return render_template("view.html", form=form, s=s, e=e, h=h)
Hope this helps!

Flask-WTForms How to override pre validate on Radio Fields

I'm trying to raise an error when a user submits without selecting a radio choice and it's not working. When I render the fields like this: {{ render_field(form.example) }} it prints the (self.gettext('PICK SOMETHING'), but when I use the format below nothing happens:
{% for subfield in form.religion %}
{{ render_field(subfield, class_="foo") }}
{{ render_field(subfield.label) }}
{% endfor %}
radio.py
class ReligionField(SelectField):
widget = ListWidget(prefix_label=False)
option_widget = RadioInput()
def pre_validate(self, form):
for v, _ in self.choices:
if self.data == v:
break
else:
raise ValueError(self.gettext('PICK SOMETHING'))
class ReligionForm(FlaskForm):
religions = ['Christian', 'Muslim', 'Hindu', 'Buddhist']
choices = [(x.lower(), x.title()) for x in sorted(religions)]
religion = ReligionField('Religion', validators=[DataRequired()], choices=choices, render_kw={"class":"post", "type":"radio"})
#app.route('/',methods=['post','get'])
def hello_world():
form = ReligionForm()
if form.validate_on_submit():
print form.religion.data
else:
print form.errors
return render_template('radio.html', form=form)
How can I override pre_validate to raise an error if I submit without selecting any choice?
Try this script:
from flask import Flask
from flask import render_template
from flask import request
from flask import flash
from flask import redirect
from flask_wtf import FlaskForm
from wtforms import SelectField
from wtforms.widgets import ListWidget, RadioInput
from wtforms.validators import DataRequired
app = Flask(__name__)
class ReligionField(SelectField):
widget = ListWidget(prefix_label=False)
option_widget = RadioInput()
def pre_validate(self, form):
for v, _ in self.choices:
if self.data == v:
break
else:
raise ValueError(self.gettext('PICK SOMETHING'))
class ReligionForm(FlaskForm):
religions = ['Christian', 'Muslim', 'Hindu', 'Buddhist']
choices = [(x.lower(), x.title()) for x in sorted(religions)]
religion = ReligionField('Religion', validators=[DataRequired()], choices=choices, render_kw={"class":"post", "type":"radio"})
#app.route('/',methods=['GET','POST'])
def hello_world():
form = ReligionForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
print form.religion.data
else:
print form.religion.errors[0]
flash(form.religion.errors[0])
return redirect('/')
return render_template('radio.html', form=form)
if __name__ == "__main__":
app.run()
radio.html
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form method="post">
{% for subfield in form.religion %}
<li>{{subfield.label}} {{subfield}} </li>
{% endfor %}
<button>Submit</button>
</form>

Flask WTForms - How to identify multiple submit buttons on the same form? [duplicate]

I have multiple form on the same page that send post request to same handler
in flask.
I am generating forms using wtforms.
what is the best way to identify which form is submitted ?
I am currently using action="?form=oneform". I think there should be some better method
to achieve the same?
The solution above have a validation bug, when one form cause a validation error, both forms display an error message. I change the order of if to solve this problem.
First, define your multiple 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 add a filter in view.py:
....
form1 = Form1()
form2 = Form2()
....
if form1.submit1.data and form1.validate(): # notice the order
....
if form2.submit2.data and form2.validate(): # 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():
"""Consider the form submitted if there is an active request and
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
"""
return _is_submitted() # bool(request) and request.method in SUBMIT_METHODS
When you call form.validate_on_submit(), it check if form is 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)!
Update
There is another way to handle multiple forms on one page. You can use multiple views to handle forms. For example:
...
#app.route('/')
def index():
register_form = RegisterForm()
login_form = LoginForm()
return render_template('index.html', register_form=register_form, login_form=login_form)
#app.route('/register', methods=['POST'])
def register():
register_form = RegisterForm()
login_form = LoginForm()
if register_form.validate_on_submit():
... # handle the register form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template('index.html', register_form=register_form, login_form=login_form)
#app.route('/login', methods=['POST'])
def login():
register_form = RegisterForm()
login_form = LoginForm()
if login_form.validate_on_submit():
... # handle the login form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template('index.html', register_form=register_form, login_form=login_form)
In the template (index.html), you need to render both forms and set the action attribute to target view:
<h1>Register</h1>
<form action="{{ url_for('register') }}" method="post">
{{ register_form.username }}
{{ register_form.password }}
{{ register_form.email }}
</form>
<h1>Login</h1>
<form action="{{ url_for('login') }}" method="post">
{{ login_form.username }}
{{ login_form.password }}
</form>
I've been using a combination of two flask snippets. The first adds a prefix to a form and then you check for the prefix with validate_on_submit(). I use also Louis Roché's template to determine what buttons are pushed in a form.
To quote Dan Jacob:
Example:
form1 = FormA(prefix="form1")
form2 = FormB(prefix="form2")
form3 = FormC(prefix="form3")
Then, add a hidden field (or just check a submit field):
if form1.validate_on_submit() and form1.submit.data:
To quote Louis Roché's:
I have in my template :
<input type="submit" name="btn" value="Save">
<input type="submit" name="btn" value="Cancel">
And to figure out which button was passed server side I have in my views.py file:
if request.form['btn'] == 'Save':
something0
else:
something1
A simple way is to have different names for different submit fields. For an
example:
forms.py:
class Login(Form):
...
login = SubmitField('Login')
class Register(Form):
...
register = SubmitField('Register')
views.py:
#main.route('/')
def index():
login_form = Login()
register_form = Register()
if login_form.validate_on_submit() and login_form.login.data:
print "Login form is submitted"
elif register_form.validate_on_submit() and register_form.register.data:
print "Register form is submitted"
...
As the other answers, I also assign a unique name for each submit button, for each form on the page.
Then, the flask web action looks like below - note the formdata and obj parameters, which help to init / preserve the form fields accordingly:
#bp.route('/do-stuff', methods=['GET', 'POST'])
def do_stuff():
result = None
form_1 = None
form_2 = None
form_3 = None
if "submit_1" in request.form:
form_1 = Form1()
result = do_1(form_1)
elif "submit_2" in request.form:
form_2 = Form2()
result = do_2(form_2)
elif "submit_3" in request.form:
form_3 = Form3()
result = do_3(form_3)
if result is not None:
return result
# Pre-populate not submitted forms with default data.
# For the submitted form, leave the fields as they were.
if form_1 is None:
form_1 = Form1(formdata=None, obj=...)
if form_2 is None:
form_2 = Form2(formdata=None, obj=...)
if form_3 is None:
form_3 = Form3(formdata=None, obj=...)
return render_template("page.html", f1=form_1, f2=form_2, f3=form_3)
def do_1(form):
if form.validate_on_submit():
flash("Success 1")
return redirect(url_for(".do-stuff"))
def do_2(form):
if form.validate_on_submit():
flash("Success 2")
return redirect(url_for(".do-stuff"))
def do_3(form):
if form.validate_on_submit():
flash("Success 3")
return redirect(url_for(".do-stuff"))
I haven't used WTForms but should work regardless. This is a very quick and simple answer; all you need to do is use different values for the submit button. You can then just do a different def based on each.
In index.html:
<div>
<form action="{{ url_for('do_stuff')}}" method="POST">
<h1>Plus</h1>
<input type = "number" id = "add_num1" name = "add_num1" required><label>Number 1</label><br>
<input type = "number" id = "add_num2" name = "add_num2" required><label>Number 2</label><br>
<input type = "submit" value = "submit_add" name = "submit" ><br>
</form>
<p>Answer: {{ add }}</p>
</div>
<div>
<form action="{{ url_for('do_stuff')}}" method="POST">
<h1>Minus</h1>
<input type = "number" id = "min_num1" name = "min_num1" required><label>Number 1</label><br>
<input type = "number" id = "min_num2" name = "min_num2" required><label>Number 2</label><br>
<input type = "submit" value = "submit_min" name = "submit"><br>
</form>
<p>Answer: {{ minus }}</p>
</div>
in app.py:
#app.route('/',methods=["POST"])
def do_stuff():
if request.method == 'POST':
add = ""
minus = ""
if request.form['submit'] == 'submit_add':
num1 = request.form['add_num1']
num2 = request.form['add_num2']
add = int(num1) + int(num2)
if request.form['submit'] == 'submit_min':
num1 = request.form['min_num1']
num2 = request.form['min_num2']
minus = int(num1) - int(num2)
return render_template('index.html', add = add, minus = minus)
Well here is a simple trick
Assume you Have
Form1, Form2, and index
Form1 <form method="post" action="{{ url_for('index',formid=1) }}">
Form2 <form method="post" action="{{ url_for('index',formid=2) }}">
Now In index
#bp.route('/index', methods=['GET', 'POST'])
def index():
formid = request.args.get('formid', 1, type=int)
if formremote.validate_on_submit() and formid== 1:
return "Form One"
if form.validate_on_submit() and formid== 2:
return "Form Two"
I normally use a hidden tag that works as an identifier.
Here is an example:
class Form1(Form):
identifier = StringField()
name = StringField('name')
submit = SubmitField('submit')
class Form2(Form):
identifier = StringField()
name = StringField('name')
submit = SubmitField('submit')
Then you can add a filter in view.py:
....
form1 = Form1()
form2 = Form2()
....
if form1.identifier.data == 'FORM1' and form1.validate_on_submit():
....
if form2.identifier.data == 'FORM2' and form2.validate_on_submit():
....
and finally in the HTML:
<form method="POST">
{{ form1.indentifier(hidden=True, value='FORM1') }}
</form>
<form method="POST">
{{ form2.indentifier(hidden=True, value='FORM2') }}
</form>
If you do it like this in the if statement it will check what was the identifier and if its equal it will run the form stuff you have in your code.
Example: Multiple WTForm in single html page
app.py
"""
Purpose Create multiple form on single html page.
Here we are having tow forms first is Employee_Info and CompanyDetails
"""
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, FloatField, validators
from wtforms.validators import InputRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Thisisasecret'
class EmployeeInfo(FlaskForm):
"""
EmployeeInfo class will have Name,Dept
"""
fullName = StringField('Full Name',[validators.InputRequired()])
dept = StringField('Department',[validators.InputRequired()])
class CompanyDetails(FlaskForm):
"""
CompanyDetails will have yearOfExp.
"""
yearsOfExp = IntegerField('Year of Experiece',[validators.InputRequired()])
#app.route('/', methods = ['GET','POST'] )
def index():
"""
View will render index.html page.
If form is validated then showData.html will load the employee or company data.
"""
companydetails = CompanyDetails()
employeeInfo = EmployeeInfo()
if companydetails.validate_on_submit():
return render_template('showData.html', form = companydetails)
if employeeInfo.validate_on_submit():
return render_template('showData.html', form1 = employeeInfo)
return render_template('index.html',form1 = employeeInfo, form = companydetails)
if __name__ == '__main__':
app.run(debug= True, port =8092)
templates/index.html
<html>
<head>
</head>
<body>
<h4> Company Details </h4>
<form method="POST" action="{{url_for('index')}}">
{{ form.csrf_token }}
{{ form.yearsOfExp.label }} {{ form.yearsOfExp }}
<input type="submit" value="Submit">
</form>
<hr>
<h4> Employee Form </h4>
<form method="POST" action="{{url_for('index')}}" >
{{ form1.csrf_token }}
{{ form1.fullName.label }} {{ form1.fullName }}
{{ form1.dept.label }} {{ form1.dept }}
<input type="submit" value="Submit">
</form>
</body>
</html>
showData.html
<html>
<head>
</head>
<body>
{% if form1 %}
<h2> Employee Details </h2>
{{ form1.fullName.data }}
{{ form1.dept.data }}
{% endif %}
{% if form %}
<h2> Company Details </h2>
{{ form.yearsOfExp.data }}
{% endif %}
</body>
</html>

Categories