I know that there are similar problems which have been answered. The csrf_enabled is not an issue now if the Form inheriting FlaskForm, and the template has the form.hidden_tag().
I have the following flask app.
## Filenname: app.py
from flask import Flask, render_template, redirect, url_for, flash, request
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, SelectField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"
class DataForm(FlaskForm):
name = StringField("Name", validators=[DataRequired()])
gender = SelectField("Gender", validators=None, choices=[(1, 'M'), (2, "F")])
submit = SubmitField("Submit", validators=None)
#app.route('/index', methods=["GET", "POST"])
def index():
form = DataForm(request.form)
print(form.validate_on_submit())
if form.validate_on_submit():
print(form.validate())
print(form.name)
flash("THIS IS FLASH")
title="hello"
return redirect(url_for('output'))
return render_template('index.html', form=form)
#app.route('/output', methods=["GET", "POST"])
def output():
title = "hello"
form = DataForm()
print(form.validate())
return render_template('output.html', title=title)
app.run(debug=False)
The following is index.html template:
<html>
<body>
{% with messages = get_flashed_messages() %}
{{ messages }}
{% endwith %}
<form action="" method="GET">
{{ form.hidden_tag() }}
{{ form.name.label }}
{{ form.name() }}
{% for error in form.name.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<hr>
{{ form.gender.label }}
{{ form.gender() }}
{{ form.submit() }}
</form>
</body>
</html>
After clicking the submit button the execution never goes in the if form.validate_on_submit() block in the index function.
I also removed all the validators, the code inside validate_on_submit block is still unreachable. Printing form.validate_on_submit() is always false.
So there are multiple problems.
Change your choices to strings:
choices=[('1', 'M'), ('2', "F")]
Change your form method to POST, because validate_on_submit() requires it:
<form action="" method="POST">
Additionally, to debug other possible errors (like CSRF), add this to your template:
{% if form.errors %}
{{ form.errors }}
{% endif %}
That fixed your code for me.
just make form without
2.form = FlaskForm(meta={'csrf': False})
Just print csrf_token with jinja and it will return True.
<form method="POST" action="#">
{{ form.csrf_token }}
</form>
Related
I am trying to create a contact form using flask, but keep getting this error when I run the app in debug mode and open the render webpage.
It's my browser error when I'm open my site in local host:
UndefinedError
jinja2.exceptions.UndefinedError: 'forms.ContactForm object' has no attribute 'message'
my files :
first.py
from flask import Flask, flash, make_response, redirect, render_template, request, session, url_for, abort
from werkzeug.utils import secure_filename
from flask_mail import Mail, Message
from forms import ContactForm
app = Flask(__name__)
app.secret_key = "example"
# Contact form
#app.route('/contact', methods=['POST', 'GET'])
def contact():
form = ContactForm()
if request.form == 'POST':
if form.validate == False:
flash('Fill all the fields')
return render_template('contact.html', form=form)
else:
return 'Success'
else:
return render_template('contact.html', form=form)
...
forms.py
from flask_wtf import Form
from wtforms import StringField, IntegerField, TextAreaField, SubmitField, RadioField, SelectField, EmailField
from wtforms import validators, ValidationError
class ContactForm(Form):
name = StringField('Name of student', [validators.data_required('Please enter your name')])
gender = RadioField('Gender', choices=[('M', 'Male'), ('F', 'Female')])
address = TextAreaField('Address')
email = EmailField('Enter your email', [validators.data_required('Please enter your email')])
age = IntegerField('Age')
language = SelectField('Languages', choices=[('cpp', 'C++'), ('py', 'Python')])
submit = SubmitField('Send')
contact.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact form</title>
</head>
<body>
<h1 style="text-align: center;" >Contact Form</h1>
{% for message in form.message.errors %}
<div>{{ message }}</div>
{% endfor %}
{% for message in form.email.errors %}
<div>{{ message|e }}</div>
{% endfor %}
<form action="/contact" method="post">
<fieldset>
<legend>Contact Form</legend>
{{ form.hidden_tag }}
{{ form.name.lable }}
{{ form.name }}
<br>
{{ form.gender.lable }}
{{ form.gender }}
<br>
{{ form.address.lable }}
{{ form.address }}
<br>
{{ form.email.lable }}
{{ form.email }}
<br>
{{ form.age.lable }}
{{ form.age }}
<br>
{{ form.submit }}
</fieldset>
</form>
</body>
</html>
form.message.errors
All part of my app is correctly work – thank you for advices
flask_wtf.Form which subclasses wtforms.form.Form has a errors field.
You want to access validation errors on the form in your template via it.
{% for field in form.errors %}
<div>{{ field }}
{% for message in form.errors[field] %}
<div>{{ message }}</div>
{% endfor %}
</div>
{% endfor %}
To access the errors for a particular form field such as the name field, you can write:
{% for message in form.name.errors %}
<div>{{ message }}</div>
{% endfor %}
I am attempting to setup hard coded authentication just for testing in my def login() route but forms.user_email.data/forms.user_password.data is returning None? I did also notice that form in my def home() is exhibiting the same behavior. I believe this is the root of multiple issues, but I'm honestly not sure. I have read multiple posts about using request - but I've been seeing other people use FlaskForm to achieve this?
Any guidance is greatly appreciated, and any general Flask tips as well - I am quite new to Python/Flask. I have been having a hard time understanding best practices/use-cases for flask/flask_restful, not to mention all the possible extensions like flask_bootstrap, FlaskForm, WTForms, FlaskLogin etc. Thanks in advance.
from flask import Flask, render_template, url_for, redirect, flash
from flask_restful import Resource, Api
from flask_wtf import FlaskForm
import requests
import pandas as pd
from flask_bootstrap import Bootstrap
from wtforms import EmailField, PasswordField, StringField
from wtforms import validators
app = Flask(__name__)
api = Api(app)
app.config['SECRET_KEY'] = 'd43b80c0727dca296c607fe8f8db478264'
bootstrap = Bootstrap(app)
class LoginForm(FlaskForm):
user_email = EmailField('Email', validators=[validators.DataRequired()])
user_password = PasswordField('Password', validators=[validators.DataRequired()])
class InputForm(FlaskForm):
ticker = StringField('Ticker', validators=[validators.DataRequired()])
class GetStonks(Resource):
def get(self, ticker):
HEADER = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:91.0) Gecko/20100101 Firefox/91.0"}
try:
response = requests.get(f"https://finviz.com/quote.ashx?t={ticker}", headers=HEADER)
result = pd.read_html(response.text)
parsed = result[5]
dict_primary = {}
dict_primary |= dict(parsed[[0,1]].values)
dict_primary |= dict(parsed[[2,3]].values)
dict_primary |= dict(parsed[[4,5]].values)
dict_primary |= dict(parsed[[6,7]].values)
dict_primary |= dict(parsed[[8,9]].values)
dict_primary |= dict(parsed[[10,11]].values)
return dict_primary, 200
except IndexError:
return {"Message": "Ticker ID not found."}
api.add_resource(GetStonks, "/api/v1/stocks/<string:ticker>")
#app.route('/')
#app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if form.user_email.data == 'admin#admin.com' and form.user_password.data == 'admin':
return redirect(url_for('home'))
else:
flash('Authentication failure', 'danger')
return render_template('login.html', title='Login', form=form)
#app.route("/home", methods=['GET', 'POST'])
def home():
form = InputForm()
return render_template('home.html', title='Home', form=form)
if __name__ == '__main__':
app.run(debug=True)
home.html
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "bootstrap/utils.html" as utils %}
{% block title %}
Home
{% endblock %}
{{super()}}
<link rel="stylesheet" href="{{url_for('.static', filename='signin.css')}}">
{% block body %}
<div class="container">
<form class="form-signin" method="POST" action="http://127.0.0.1:5000/api/v1/stocks/{{url}}">
<h2 class="form-signin-heading">INPUT TICKER BELOW</h2>
<br>
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{{ wtf.form_field(form.ticker) }}
<button class="btn btn-lg btn-primary" type="submit">GET</button>
</form>
</div>
{% endblock %}
login.html
{% extends "bootstrap/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "bootstrap/utils.html" as utils %}
{% block title %}
Login
{% endblock %}
{{super()}}
<link rel="stylesheet" href="{{url_for('.static', filename='signin.css')}}">
{% block content %}
<div class="container">
<form class="form-signin" method="POST" action="{{ url_for('home') }}">
<h2 class="form-signin-heading">SIMPLE STOCK API</h2>
<br>
<br>
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{{ wtf.form_field(form.user_email) }}
{{ wtf.form_field(form.user_password) }}
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
{% endblock %}
Inside your login.html instead of:
<form class="form-signin" method="POST" action="{{ url_for('home') }}">
Action should be login instead of home. So it should look like this:
<form class="form-signin" method="POST" action="{{ url_for('login') }}">
What I am aiming to achieve: once logged in with the correct credentials on the /login page, the user is redirected to the /camera page.
Actual result: /login page just reloaded, with form inputs wiped.
No Error Messages / exceptions
I've looked through the code but can't understand why it's doing this. Any help appreciated.
main.py:
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from flask_login import LoginManager, login_required
from flaskconfig import Config
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired
from werkzeug.security import check_password_hash
app = Flask(__name__)
app.config.from_object(Config)
logindatabase = {
'User1': 'pbkdf2:sha256:150000$QLfB2gVe$fd76b36df43686e7a8e27a9066331727e246f0d2835688d8d303af2f9b13a1cc',
'User2': 'pbkdf2:sha256:150000$4rK2hvN8$d4940c3dac1ce6f293bc495934fce92cb840c470c83eb0e3b864f407eec85868'
}
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Sign In')
#app.route('/')
def home():
return redirect(url_for('login'))
#app.route('/login', methods = ['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = form.username.data
if user is None or user not in logindatabase.keys() or check_password_hash(logindatabase[user], form.password.data) == False:
return redirect(url_for('login'))
else:
login_user(user)
return redirect(url_for('camera'))
return render_template('login.html', title = 'Log In', form = form)
#app.route('/camera')
#login_required
def camera():
return render_template('camera.html')
if __name__ == '__main__':
app.run(host = '0.0.0.0')
Note: runs on host 0.0.0.0 as I am developing on repl.it (intentional)
templates > login.html:
<!DOCTYPE html>
<html>
<head>
<title>Login Page - Door Camera System</title>
</head>
<body>
<h1>Welcome!</h1>
<hr>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for msg in messages %}
<li>
{{ msg }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}
<h2>Sign In</h2>
<form action="" method="get" novalidate>
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
</p>
<p>{{ form.submit() }}</p>
{{ form.csrf_token }}
</form>
{% endblock %}
</body>
</html>
templates > camera.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
</head>
<body>
<input type="checkbox" class='toggle' checked data-toggle="toggle">
<div class='status'>System Active</div>
</body>
<script>
$(document).ready(function() {
$('.toggle').click(function() {
var toggleval = $('.status').text();
if (toggleval === 'System Disabled'){
$('.status').html('System Active');
}
else{
$('.status').html('System Disabled');
}
});
});
</script>
</html>
flaskconfig.py:
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'flaskwebsecretkey5000'
Thanks so much in advance!!
Method should be POST and removed novalidate.
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
</p>
<p>{{ form.submit() }}</p>
{{ form.csrf_token }}
</form>
I need to add 2 separate forms to the same web page and I cannot get the second form to output any information.
In my research I saw people suggesting to split the forms onto 2 different def functions but I am having trouble figuring out how to do that an keep both forms usable at the sane time.
from flask import Flask, session, render_template, url_for, redirect
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
app = Flask(__name__)
app.config['SECRET_KEY'] = 'b317a06ad972917a84be4c6c14c64882'
class PostForm(FlaskForm):
content = StringField('Content')
submit = SubmitField('Submit')
class SecondPostForm(FlaskForm):
content = StringField('Second Content')
submit = SubmitField('Second Submit')
#app.route("/", methods=['GET', 'POST'])
#app.route("/home", methods=['GET', 'POST'])
def home():
form = PostForm()
second_form = SecondPostForm()
if form.validate_on_submit():
print(form.content.data)
session['content'] = form.content.data
redirect(url_for('submit'))
return redirect(url_for('submit'))
'''
--------------------------------------------------------------------
is it possible to split the second if statement onto its own def and keep
them both usable on the same page at the same time?
--------------------------------------------------------------------
'''
elif second_form.validate_on_submit():
print(second_form.content.data)
session['content'] = second_form.content.data
return redirect(url_for('othersubmit'))
return render_template('example.html', second_form=second_form, form=form)
#app.route("/submit", methods=['GET', 'POST'])
def submit():
content = session.get('content', None)
print(content)
session.pop('content', None)
return redirect(url_for('home'))
#app.route("/othersubmit", methods=['GET', 'POST'])
def othersubmit():
print('othersubmit')
content = session.get('content', None)
print(content)
session.pop('content', None)
return redirect(url_for('home'))
if __name__ == "__main__":
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ legend }}</legend>
<div class="form-group">
{{ form.content.label(class="form-control-label") }}
{% if form.content.errors %}
{{ form.content(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.content.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.content(class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
<form method="POST" action="{{ url_for('othersubmit') }}">
{{ second_form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ legend }}</legend>
<div class="form-group">
{{ second_form.content.label(class="form-control-label") }}
{% if second_form.content.errors %}
{{ second_form.content(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in second_form.content.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ second_form.content(class="form-control form-control-lg") }}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ second_form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
</body>
</html>
I have tried with and without action="{{ url_for('othersubmit') }}" neither have the desired result
The goal is to have either form print its own data and also print which form it came from. At the moment the 1st form prints the same data twice and the 2nd form doesn't print anything.
You can do few things:
1) Firstly, if you have 2 forms on one page, you can't have two {{ second_form.hidden_tag() }}, as they will throw error of having same id. As per wtform documentation, I have handled it as {{ form.csrf_token(id='login_csrf') }}. See you can specify your own id, so this will prevent clash.
2) Change the Submit button name in both the forms. So that they are distinct. Then you can do as below. As you can see i have 2 forms on same page form_login and form_reg. I have kept submit buttons name differet. One is login and another is register.
if form_login.login.data:
if form_login.validate_on_submit():
#do something here
elif form_reg.register.data:
if form_reg.validate_on_submit():
#do something here
return render_template('abc.html')
This should work for you.
I have this small Flask program. It expects the name and age of the user, and then it prints a message. Really easy, for I'm just getting started with Python and Flask.
from flask import Flask, render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.wtf import Form
from wtforms import StringField, IntegerField, SubmitField
from wtforms.validators import Required, Length, NumberRange
app = Flask(__name__)
app.config['SECRET_KEY'] = 'top secret!'
bootstrap = Bootstrap(app)
class NameForm(Form):
name = StringField('What is your name?', validators = [Required(), Length(1, 16)])
age = IntegerField('How old are you?', validators = [Required(), NumberRange(min=1, max=99, message="Should be between 1 and 99")])
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
name = None
age = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
age = form.age.data
form.age.raw_data = None
form.name.data = ''
return render_template('index.html', form = form, name = name, age = age)
if __name__ == '__main__':
app.run(debug = True)
And, for the HTML template, I have:
{% extends "bootstrap/base.html" %}
{% block title %}Form Example{% endblock %}
{% block navbar %}
<nav class="navbar navbar-inverse" role="navigation">
<div class="container">
Hello
</div>
</nav>
{% endblock %}
{% block content %}
<div class="container">
<form method="POST" action="">
{{ form.name.label }} {{ form.name(size=16, class='name-field') }}
{% for error in form.name.errors %}
{{ error }}
{% endfor %}
<br>
{{ form.age.label }} {{ form.age(class='age-field') }}
{% for error in form.age.errors %}
{{ error }}
{% endfor %}
<br />
{{ form.submit() }}
{{ form.hidden_tag() }}
</form>
{% if name %}
<h1>Hello, {{ name.capitalize() }}</h1>
<h2>You're {{ age }} years old.</h2>
{% endif %}
</div>
{% endblock %}
My question is, after I submit the form, the name input is emptied but the age input is not. Why? How can I empty a numeric field?
Thanks.
Thanks for the clear question-- the easiest way I can think of to blank the form is just to create it and tell it to ignore the request form data.
#app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm() # Auto-populates from request data.
name = None
age = None
if form.validate_on_submit():
name = form.name.data
age = form.age.data
# Lets now create a form, but ignore the request data, so it's empty:
form = NameForm(formdata=None)
return render_template('index.html', form = form, name = name, age = age)
Also, I notice you're using Required() that's in the progress of being replaced by DataRequired() and will disappear in WTF v3 so you might want to start using it now, your future self will thank you!