I am creating a form using Flask-WTForms.
I am using a BooleanField so that a user can indicate they agree terms.
I cannot validate the BooleanField upon submission to ensure that it has been checked. I have tried using Required(), DataRequired() and custom validation but in each case I have not received a validation error.
Here are the nuts and bolts of the application:
from flask import Flask, render_template, session, redirect, url_for, flash
from flask_wtf import Form
from wtforms import BooleanField, SubmitField
from wtforms.validators import Required, DataRequired
from flask_bootstrap import Bootstrap
app = Flask(__name__)
app.config['SECRET_KEY'] = 'impossibletoknow'
bootstrap = Bootstrap(app)
class AgreeForm(Form):
agreement = BooleanField('I agree.', validators=[DataRequired()])
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
form = AgreeForm()
if form.validate_on_submit():
agreement = form.agreement.data
if agreement is True:
flash('You agreed!')
return redirect(url_for('index', form=form))
form.agreement.data = None
agreement = False
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
And here is the index.html template...
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
Any suggestions would be gratefully received.
Works for me— you do need to use DataRequired() (Required is being deprecated):
from flask import Flask, render_template
from flask_wtf import Form
from wtforms import BooleanField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.secret_key = 'STACKOVERFLOW'
class ExampleForm(Form):
checkbox = BooleanField('Agree?', validators=[DataRequired(), ])
#app.route('/', methods=['post', 'get'])
def home():
form = ExampleForm()
if form.validate_on_submit():
return str(form.checkbox.data)
else:
return render_template('example.html', form=form)
if __name__ == '__main__':
app.run(debug=True, port=5060)
Template:
<form method="post">
{{ form.hidden_tag() }}
{{ form.checkbox() }}
<button type="submit">Go!</button>
</form>
<h1>Form Errors</h1>
{{ form.errors }}
You do not have to include DataRequired() in your form, because it does not make sense, being a boolean. You must take of the incoming form data in the post method by saying if true.
Related
thank you so much for your help.
index.html
{% extends "base.html" %}
{% block content %}
<form action="{{ url_for('details') }}" method="POST">
{{ form.firstName.label }} {{form.firstName}}
<br>
{{form.submit}}
</form>
{% endblock %}
when click on submit button i get Method Not Allowed The method is not allowed for the requested URL. looks like the action attribute in the above code is not redirecting to details function which will take to details view page when submit button is clicked but when i manually type details in the address bar it redirects to details view page with no results. wondering why it button click is not working
details.html
{% extends "base.html" %}
{% block content %}
{{session['firstName']}}
{% endblock %}
p.py
from flask import Flask
from flask import render_template, redirect, url_for, session
from flask_wtf import FlaskForm
from wtforms import StringField, EmailField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo
app = Flask( __name__ )
app.config["SECRET_KEY"] = "for test"
class userInfo(FlaskForm):
firstName = StringField("enter first name:", validators=[DataRequired()])
submit = SubmitField("Submit")
#app.route("/", methods=["POST", "GET"])
def index():
form = userInfo()
if form.validate_on_submit():
session['firstName'] = form.firstName.data
form.firstName.data = ''
return redirect(url_for("details"))
return render_template("index.html", form=form)
#app.route("/details")
def details():
return render_template("details.html")
if __name__ == "__main__":
app.run(debug=True)
i want to navigate to details view page when my form in index view page is submitted but when button is clicked i get error message instead of navigating to different view page.
From the documentation:
By default, a route only responds to a GET request. You can use
the method's argument of the route() decorator to handle different HTTP
methods.
(https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods)
You should add a decorator argument methods=["POST"] to receive a POST request.
This question already has an answer here:
Form is never valid with WTForms
(1 answer)
Closed 3 years ago.
I have like a search bar built with wtforms, but I don't know for some reason I am unable to fetch data or value entered in the search bar. I tried almost everything from online tutorials but just couldn't find the solution. Below is my code examples.
For my search bar, I have tried form.search.data and request.form['search'] too but it's not working.
#flask_app.py
from flask import Flask, render_template, url_for, flash, redirect, request
from forms import SearchForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '791628bb0b13ce0c676dfde'
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html')
#app.route('/search', methods= ['GET', 'POST'])
def search():
form = SearchForm()
if form.validate_on_submit():
if form.search.data == 'admin':
return redirect(url_for('home'))
return render_template('search.html', form= form)
if __name__ == "__main__":
app.run(debug=True)
#forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class SearchForm(FlaskForm):
search = StringField('search', validators=[DataRequired()])
submit = SubmitField('Search')
#search.html
{% extends 'layout.html' %}
{% block content %}
<form action="" class="form-inline mt-4" method="POST">
<div class="form-group">
{{form.search(class= 'form-control', placeholder= 'Enter term to search')}}
{{form.submit(class= 'btn btn-outline-primary')}}
</div>
</form>
{%endblock%}
#home.html
{% extends 'layout.html' %}
{% block content %}
<h1>Welcome to homepage!</h1>
{%endblock%}
Is there any bug? I expect form.search.data should be able to fetch data from search bar.
You're not inserting the CSRF field in the HTML form which can be done via form.hidden_tag()
#search.html
{% extends 'layout.html' %}
{% block content %}
<form action="" class="form-inline mt-4" method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{form.search(class='form-control', placeholder= 'Enter term to search')}}
{{form.submit(class='btn btn-outline-primary')}}
</div>
</form>
{% endblock %}
See this
I'm trying to raise an error in Jinja2, in a WTForm, the error should be raised if url input is not validated, but when i submit an invalide url, i get a popup saying "Please enter a url".
how do i pass the default popup and add a custom error message ?
here is the main py:
from datetime import datetime
from flask import Flask, render_template, url_for, request, redirect,flash
from logging import DEBUG
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from flask.ext.wtf.html5 import URLField
from wtforms.validators import DataRequired , url
app = Flask(__name__)
app.logger.setLevel(DEBUG)
app.config['SECRET_KEY']='{#\x8d\x90\xbf\x89n\x06%`I\xfa(d\xc2\x0e\xfa\xb7>\x81?\x86\x7f\x1e'
#app.route('/')
#app.route('/index')
def index():
return render_template('base.html')
#app.route('/add', methods=['GET','POST'])
def add():
return render_template('add.html')
# HERE IS THE LOGIN FORM
class Login(FlaskForm):
username = StringField('username')
password = PasswordField('password')
url = URLField('url', validators=[DataRequired(),url()])
#app.route('/form', methods=['GET','POST'])
def form():
form = Login()
if form.validate_on_submit():
url = form.url.data
return redirect(url_for('index'))
return render_template('form.html',form = form )
if __name__ =='__main__':
app.run(debug=True)
and here is the template:
<!DOCTYPE html>
<html>
<head>
<title>form</title>
</head>
<body>
<h1>Hello !</h1>
<form method="POST" action="{{url_for('form')}}">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}
{{ form.password.label }}
{{ form.password }}
{{ form.url.label }}
{{ form.url }}
{% if form.url.errors %} <p> {{error}}</p> {% endif %}
<button type="submit">Submit</button>
</form>
</body>
</html>
Because you're using the data type URLField, this is rendered as a HTML5 "url" form field type.
Your browser recognises this and performs its own validation on the data submitted:
There is no way for you to override this - it's built in to the browser.
If you need to show a custom error message, you might be able to use a TextField instead, and provide your own URL validation logic.
Add your own message instead of default message in your form defination.
url = URLField('url', validators=[DataRequired(),url(message="Please enter a valid url (e.g.-http://example.com/)")])
As Matt Healy before mentiones, it is the browser that validates URLField.
So if you want a custom error message use StringField (TextField is outdated). If required, a custom message can be used as shown below message='text to display'.
Example:
class XYZForm(FlaskForm):
url = StringField('url', validators=[DataRequired(),url(message='Please enter valid URL')])
description = StringField('description')
Of course the *.html should include code to output an error to the page:
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
It seems like novalidate attribute works for your case.
I have a page with multiple links to redirect the user to different pages. I thought using a form would be nicer, so I defined a WTForms Form with multiple SubmitFields. How do I determine which button was clicked and redirect based on that?
class MainForm(Form):
user_stats = SubmitField('User Stats')
room_stats = SubmitField('Room Stats')
#main.route('/')
#login_required
def index():
form = MainForm()
return render_template('index.html', form=form)
<form action="#" method="post">
{{ wtf.quick_form(form) }}
</form>
You added two buttons to the form, so check which of the fields' data is True.
from flask import Flask, render_template, redirect, url_for
from flask_wtf import Form
from wtforms import SubmitField
app = Flask(__name__)
app.secret_key = 'davidism'
class StatsForm(Form):
user_stats = SubmitField()
room_stats = SubmitField()
#app.route('/stats', methods=['GET', 'POST'])
def stats():
form = StatsForm()
if form.validate_on_submit():
if form.user_stats.data:
return redirect(url_for('user_stats'))
elif form.room_stats.data:
return redirect(url_for('room_stats'))
return render_template('stats.html', form=form)
app.run(debug=True)
<form method="post">
{{ form.hidden_tag() }}
{{ form.user_stats }}
{{ form.room_stats }}
</form>
I am trying to create a contact form using flask but keep getting this error when the page is rendered.
'forms.ContactForm object' has no attribute 'hidden_tag'
Here are my files:
contact.html
{% extends "layout.html" %}
{% block content %}
<h2>Contact</h2>
<form action="{{ url_for('contact') }}" method=post>
{{ form.hidden_tag() }}
{{ form.name.label }}
{{ form.name }}
{{ form.email.label }}
{{ form.email }}
{{ form.subject.label }}
{{ form.subject }}
{{ form.message.label }}
{{ form.message }}
{{ form.submit }}
</form>
{% endblock %}
forms.py
from flask.ext.wtf import Form
from wtforms import Form, TextField, TextAreaField, SubmitField, validators
class ContactForm(Form):
name = TextField("Name", [validators.Required()])
email = TextField("Email",[validators.Required(), validators.email()])
subject = TextField("Subject", [validators.Required()])
message = TextAreaField("Message", [validators.Required()])
submit = SubmitField("Send")
routes.py
from flask import Flask, render_template, request
from forms import ContactForm
app = Flask(__name__)
app.secret_key = 'development key'
#app.route('/')
def home():
return render_template('home.html')
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/contact', methods=['GET', 'POST'])
def contact():
form = ContactForm()
if request.method == 'POST':
return 'Form posted.'
elif request.method == 'GET':
return render_template('contact.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
All the other page templates are working perfectly fine. Any advice would be awesome! Thanks for the help!
I just fixed this problem as well.
Your problem is that you imported Form twice, rendering your flask-wtf Form import useless.
from flask_wtf import Form
from wtforms import Form, TextField, TextAreaField, SubmitField, validators
# ^^^ Remove
Only the flask-wtf extension has the special Form class which can handle CSRF automatically / other stuff.
I tried to fix this, too.
After removing brackets "()" appended after hidden_tag, it works.
{{ form.hidden_tag }}
It took me some time to fix this.
First import Form, fields, bootstrap as:
from flask_wtf import Form
from wtforms import StringField #etc
from flask_bootstrap import Bootstrap
Config secret key and bootstrap
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key'
Bootstrap(app)
create your form as your used to:
class ContactForm(Form):
name = TextField("Name", [validators.Required()])
email = TextField("Email",[validators.Required(), validators.email()])
subject = TextField("Subject", [validators.Required()])
message = TextAreaField("Message", [validators.Required()])
submit = SubmitField("Send")
Nothing special in the routing aswell, just return it normaly.
In the html:
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% if form %}
{{ wtf.quick_form(form, ) }}
{% endif %}
And that's it. Hope you find some (or all) of it useful.
Update for #Yuji'Tomita'Tomita answer :
You should import FlaskForm instead of Form
from flask_wtf import FlaskForm
from wtforms import TextField, TextAreaField, SubmitField, validators
The error that you're seeing is telling you that forms.ContactForm has no method called "hidden_tag". You're referencing that method on the 6th line of contact.html like this:
{{ form.hidden_tag() }}
According to the flask documentation, this is the correct way to implement CSRF protection.
I would start by removing the line that references "form.hidden_tag()", then see if your form works. Then go back and implement CSRF protection per those instructions from the documentation.