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.
Related
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.
In my Flask app I have a form generated with wtforms and jinja templates. If validation passes I want to redirect in a new tab, else I want to stay on the same page and display the errors. However if I set target="_blank" in my form, it opens a new tab without validation passing and shows the errors there. Removing target="_blank" will not open a new tab. Is there a way of achieving this without rewriting the whole validation in js? Thanks!
Code:
from wtforms import Form, TextAreaField, StringField, validators
class SubmitForm(Form):
field1 = StringField(u'field1', [validators.DataRequired()])
field2 = TextAreaField(u'field2', [validators.DataRequired()])
#app.route('/', methods=['POST'])
def sub():
form = SubmitForm(request.form)
if request.method == 'POST' and form.validate():
# great success
return redirect('/my_redirect_uri')
return render_template('index.html', form=form)
#app.route('/')
def layout():
return render_template('index.html', form=SubmitForm())
index.html:
{% from "_formhelpers.html" import render_field %}
<form method=post action="/" target="_blank">
<dl>
{{ render_field(form.field1) }}
{{ render_field(form.field2) }}
</dl>
<p><input type=submit value=Submit>
</form>
_formhelpers.html(not that relevant but for the sake of completness):
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Main issue
You must pass the form object as well as the variables representing your form fields namely field1 and field2.
Details
What is being stated above means that you need to change:
return redirect('/my_redirect_uri')
to
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
This also means that you have to adjustments to your view method:
#app.route('/', methods=['POST'])
def sub():
form = SubmitForm(request.form)
if request.method == 'POST' and form.validate():
# great success
field1 = form.field1.data
field2 = form.field2.data
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
return render_template('index.html', form=form)
Now there are some improvements to your program:
Code less principle:
Replace if request.method == 'POST' and form.validate(): by if form.validate_on_submit(): (you save one instruction)
Use url_for():
For the sake of scalability because in future versions and reedits of your program, any changes made to route names will be automatically available when using url_for() as it generates URLs using the URL map. You may use it in return redirect('/my_redirect_uri') (you may check the link I provided for further information)
Use sessions:
Sessions will make your application "able to remember" the form data being sent. In your example, you can use a session this way:
session['field1'] = form.field1.data
session['field2'] = form.field2.data
This means, for example, the above line:
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
must be changed to:
return redirect('my_redirect_uri', form=form, session.get('field1'), session.get('field2'))
Note that if you want to implement sessions, you will need to set a secret key because they are stored on the client side, so they need to be protected (cryptographically in Flask's philosophy). This mean you must configure your application this way:
app.config['SECRET_KEY'] = 'some secret phrase of your own'
Actually, the problem is not related to the browser tab you opened. The whole issue emanates from the redirect() statement.
More details on why it is good to use sessions? Check the next last section:
A word about redirect:
Your POST request is handled by redirect, consequently you loose access to the form data when the POST request ends.
The redirect statement is simply a response which, when received, the browser issues a GET request. This mechanism is there mainly for the following (and similar) situation:
If you try to refresh the same browser window on which you submitted the form data, the browser will prompt you a pop up window to confirm that you want to send the data (again) because the browser remembers, by design, the last request it performed. Of course, this is nasty for the user of your application. There we need sessions. This is also helpful for the browser tab to which you redirect.
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.
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.