In this Script, I set the message for InputRequired in wtforms.validator to showing error. also, I giving max and min for a length of character. But these arguments not working.
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, SubmitField,StringField
from wtforms.validators import ValidationError, Length, EqualTo, InputRequired, Email
class Login(FlaskForm):
email =StringField(validators=[InputRequired(message = 'please input something'), Length(min=10, max=20), Email(message='this is not email')])
password = PasswordField(validators=[InputRequired(message='please input something'), Length(min=8, max=20, message='you must be input more than 8 character')])
submit = SubmitField('Enter')
class Register(FlaskForm):
email =StringField(validators=[InputRequired(message = 'please input something'), Length(min=10, max=20), Email(message='this is not email')])
phone = IntegerField(validators=[InputRequired(message='please input something')])
password = PasswordField(validators=[InputRequired(message='please input something'), Length(min=8, max=20, message='you must be input more than 8 character')])
submit = SubmitField('Enter')
This Script is login.html
{% extends "base.html" %}
{% block content %}
<h3>this is Home page</h3>
<form action="#">
<!-- {{forlogin.hidden_tag()}} -->
{{forlogin.email(placeholder="email")}}
<br>
{{forlogin.password(placeholder="password")}}
<br>
{{forlogin.submit}}
</form>
{% endblock content %}
From the Field class documentation in WTForms documentation:
errors
If validate encounters any errors, they will be inserted into this list.
So to show the errors, you have to show the items in the list on your page:
<form action="#">
{{login.email(placeholder="email")}}{% for error in login.email.errors %} {{ error }} {% endfor%}
</form>
I do not know what the for was meant to do between the double braces, I removed it. It caused errors in Jinja2.
Related
I have been trying to incorporate wtforms validators inside 2 forms I have on my index.html(this is a Uni assignment). Everything worked well before I did anything, but security was very bad. After I have inserted some wtform validators, everything works OK. But usability is quite bad. If I get an error whilst filling out one of the forms, I get error messages not only in the form I was writing stuff in, but also in the other separate form on index.html.
I suspect it has something with the way the assignment is set up. Like I said, I have 2 forms on my index.html. In a separate python file, where all the forms are configured, there is the following setup (where LoginForm and RegisterForm are the two forms on index.html):
class LoginForm(FlaskForm):
#more code here
class RegisterForm(FlaskForm):
#more code here
class IndexForm(FlaskForm):
login = FormField(LoginForm)
register = FormField(RegisterForm)
My first thought was to just delete the IndexForm code (and obviously update the code everywhere else where it was needed). I can´t see why the class IndexForm has to wrap the other 2. But that didn´t work out. So I suspect there is a reason why it´s set up the way it is. But how can I make RegisterForm and LoginForm behave such that a validation error in one of them, doesn´t trigger error messages in the other? I have inserted more verbose code below, if that could be of any help.
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, FormField, TextAreaField, FileField
from wtforms.fields.html5 import DateField
from wtforms.validators import InputRequired, Length, EqualTo, NoneOf
# defines all forms in the application, these will be instantiated by the template,
# and the routes.py will read the values of the fields
# TODO: Add validation, maybe use wtforms.validators??
# TODO: There was some important security feature that wtforms provides, but I don't remember what; implement it
#invalidInput = [ADD THINGS HERE]
class LoginForm(FlaskForm):
username = StringField('Username', render_kw={'placeholder': 'Username'}, validators=[InputRequired()])
password = PasswordField('Password', render_kw={'placeholder': 'Password'}, validators=[InputRequired()])
remember_me = BooleanField('Remember me') # TODO: It would be nice to have this feature implemented, probably by using cookies
submit = SubmitField('Sign In')
class RegisterForm(FlaskForm):
first_name = StringField('First Name', render_kw={'placeholder': 'First Name'}, validators=[InputRequired(), NoneOf(invalidInput, message="Invalid input")])
last_name = StringField('Last Name', render_kw={'placeholder': 'Last Name'}, validators=[InputRequired()])
username = StringField('Username', render_kw={'placeholder': 'Username'}, validators=[
InputRequired(), Length(min=5, max=50, message="Must be between 5 and 50 characters")])
password = PasswordField('Password', render_kw={'placeholder': 'Password'}, validators=[
InputRequired(), Length(min=8, max=50, message="Must be between 8 and 50 characters"), EqualTo('confirm_password', message='Passwords must match')])
confirm_password = PasswordField('Confirm Password', render_kw={'placeholder': 'Confirm Password'}, validators=[
InputRequired(), Length(min=8, max=50, message="Must be between 8 and 50 characters")])
submit = SubmitField('Sign Up')
class IndexForm(FlaskForm):
login = FormField(LoginForm)
register = FormField(RegisterForm)
routes.py
from app import app, query_db
from app.forms import IndexForm, PostForm, FriendsForm, ProfileForm, CommentsForm
from datetime import datetime
import os
#############################
from flask_wtf import FlaskForm
from wtforms import (StringField, PasswordField, SubmitField, TextAreaField, IntegerField, BooleanField, RadioField)
from wtforms.validators import InputRequired, Length
# this file contains all the different routes, and the logic for communicating with the database
# home page/login/registration
#app.route('/', methods=['GET', 'POST'])
#app.route('/index', methods=['GET', 'POST'])
def index():
form = IndexForm()
if form.login.validate_on_submit() and form.login.is_submitted() and form.login.submit.data:
user = query_db('SELECT * FROM Users WHERE username="{}";'.format(form.login.username.data), one=True)
print("user is ", user)
if user == None:
flash('Sorry, this user does not exist!')
elif user['password'] == form.login.password.data:
return redirect(url_for('stream', username=form.login.username.data))
else:
flash('Sorry, wrong password!')
elif form.register.validate_on_submit() and form.register.is_submitted() and form.register.submit.data:
flash("New user registered!")
query_db('INSERT INTO Users (username, first_name, last_name, password) VALUES("{}", "{}", "{}", "{}");'.format(form.register.username.data, form.register.first_name.data,
form.register.last_name.data, form.register.password.data))
print(form.register.username.data)
print(form.register.password.data)
return redirect(url_for('index'))
return render_template('index.html', title='Welcome', form=form)
index.html
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Social Insecurity</h1>
<p class="lead">The social network for the insecure™</p>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-lg-6">
<div class="card text-center">
<div class="card-header">
Sign In
</div>
<div class="card-body">
<h5 class="card-title">Access an existing profile</h5>
<form action="" method="POST" novalidate>
{{ wtf.form_field(form.login.username) }}
{{ wtf.form_field(form.login.password) }}
{{ wtf.form_field(form.login.remember_me) }}
{{ wtf.form_field(form.login.submit, class="btn btn-primary") }}
</form>
</div>
</div>
</div>
<div class="col-sm-12 col-lg-6">
<div class="card text-center">
<div class="card-header">
Register
</div>
<div class="card-body">
<h5 class="card-title">Create a new profile</h5>
<form action="", method="POST" novalidate>
{{ wtf.form_field(form.register.csrf_token) }}
{{ wtf.form_field(form.register.first_name) }}
{{ wtf.form_field(form.register.last_name) }}
{{ wtf.form_field(form.register.username) }}
{{ wtf.form_field(form.register.password) }}
{{ wtf.form_field(form.register.confirm_password) }}
{{ wtf.form_field(form.register.submit, class="btn btn-primary") }}
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}```
Solved it by removing the validators from LoginForm in forms.py. No need to validate these fields, if I have already validated them when creating a new user.
I have used the built-in validators, but none of them is printing a message on the page. Also, I want to create a custom validator to check duplicate username. I have written the function, as I am a beginner, I don't know how to use it. Pls resolve the problem.
from flask import Flask, app, render_template, request, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import InputRequired, EqualTo, ValidationError
app = Flask(__name__)
app.config['SECRET_KEY'] = "PQrs12t46uvvrty567"
class MyForm(FlaskForm):
username = StringField('Username', validators=[InputRequired(message="This field is required.")])
password=PasswordField('Password', validators=[InputRequired(message=("enter the password"))])
confirm_password=PasswordField('Confirm Password', validators=[EqualTo('password')])
submit = SubmitField('Register')
def isDuplicate():
appdatabase={"Alis":"Smith","Mike":"Brown","Paul":"Miller"}
form = MyForm()
for user in appdatabase:
if form.username == appdatabase[user]:
raise ValidationError("Username already exists! Please choose another one.")
#app.route('/')
def base():
form = MyForm()
return render_template('customvalidator.html', form = form)
#app.route('/submitform', methods=["GET","POST"])
def submitform():
form = MyForm()
if form.validate_on_submit():
return 'Form accepted successfully.'
else:
return 'Incorrect form data'
if __name__=="__main__":
app.run(debug=True)
HTML file
<!DOCTYPE html>
<html>
<head><title>My website</title></head>
<body>
<h1>Registration form</h1>
<form action="{{ url_for('submitform') }}" method="post">
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}
<ul>
{% for error in form.username.errors %}
<li style="color: red;">{{ error }} </li>
{% endfor %}
</ul>
<br>
{{ form.password.label }}
{{ form.password }} <br><br>
{{ form.confirm_password.label }}
{{ form.confirm_password }} <br><br>
{{ form.submit}}
</form>
</body>
</html>
Try changing the isDuplicate function to this:
def isDuplicate(form, field):
appdatabase={"Alis":"Smith","Mike":"Brown","Paul":"Miller"}
form = MyForm()
for user in appdatabase:
if form.username.data == appdatabase[user]:
raise ValidationError("Username already exists! Please choose another one.")
Notice the added form and field parameters to the function to allow its use as a custom validator. form.username was also changed to form.username.data to access the data of the username field. (This whole function will need to be defined before the form.)
You will then need to add this custom validator to your list of validators for the username field in your form like so:
username = StringField('Username', validators=[InputRequired(message="This field is required."),isDuplicate])
With these changes, the custom validation should work, but we still haven't solved the issue of the custom error messages. To do this, add a novalidate attribute to your HTML form tag. Adding this will disable the default validation on forms and allow you to display custom messages.
With that in place, I believe the messages still won't work because of how you are handling the form submission. When the form does not validate, you will want to display the same form template instead of showing them whether it submitted or not.
My personal opinion is that merging your two form routes will be the simplest option. Have a route define the form, check for when it gets validated or not, and then render the form template. This will enable you to keep everything together and not render a different template when you only want to display the same template with a few added error messages.
Credits:
Custom validators (check the last point)
SO question on displaying validation messages
-which led me to this one on actually how to disable the default messages.
I'm using flask wtforms with input validation. Everything's working fine, except with validation failure my ValidationError message is not getting displayed to the user...
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField, ValidationError # URLField, EmailField
from wtforms.validators import DataRequired, Email, EqualTo
class SignupForm(FlaskForm):
name = StringField("Name:", validators=[DataRequired()])
email = StringField("Email:", validators=[DataRequired(), Email()])
password = PasswordField("Password:", validators=[DataRequired(), EqualTo('pass_confirm', message='Passwords must match')])
pass_confirm = PasswordField("Confirm:", validators=[DataRequired()])
submit = SubmitField("Create Account")
def validate_email(self, field):
if User.query.filter_by(email=field.data).first():
raise ValidationError('Your email has already been registered.')
def validate_name(self, field):
if User.query.filter_by(name=field.data).first():
raise ValidationError('This username is taken.')
and in my view:
#users.route('/signup', methods=['GET','POST'])
def signup():
form = SignupForm()
if form.validate_on_submit():
url = form.name.data # todo: generate unique url
user = User(name=form.name.data,
url=url,
email = form.email.data,
password = form.password.data)
db.session.add(user)
db.session.commit()
return redirect(url_for('users.login'))
return render_template('signup.html', form=form)
and this is the template:
<div class="jumbotron">
<h1>Sign up Page</h1>
<p>Please fill out the form</p>
<form method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.name.label(class='form-group') }}
{{ form.name(class='form-control') }}
</div>
<div class="form-group">
{{ form.email.label(class='form-group') }}
{{ form.email(class='form-control') }}
</div>
<div class="form-group">
{{ form.password.label(class='form-group') }}
{{ form.password(class='form-control') }}
</div>
<div class="form-group">
{{ form.pass_confirm.label(class='form-group') }}
{{ form.pass_confirm(class='form-control') }}
</div>
{{ form.submit(class='btn btn-primary') }}
</form>
</div>
Upon submitting an invalid form (either invalid email or name) the form simply returns blank, without indicating what the user did wrong. How can I display those validationerrors to the user? Much appreciated!
form must get some data
#users.route('/signup', methods=['GET','POST'])
def signup():
form = SignupForm(request.form)
or
#users.route('/signup', methods=['GET','POST'])
def signup():
form = SignupForm(request.json)
I believe you are importing ValidationError incorrectly
Try this importing it from wtforms.validators like this->
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField # URLField, EmailField
from wtforms.validators import DataRequired, Email, EqualTo, ValidationError
In addition to this, you can add flash messages to your screen from your routes code such as this.
from flask import flash
if form.validate_on_submit():
flash(f'Info was saved to the database for user {form.name.data}', success)
else:
flash('Failed to save to database', 'danger')
I'm trying to setup a flask webapp with some basic login/register system controlled by flask-WTF forms.
Here is my code:
html
<!-- Register form -->
<div class="form">
<div class="form-area">
<h2>Register</h2>
<form action="{{ url_for('register') }}">
{{ form.csrf_token() }}
{{ form.hidden_tag() }}
{{ form.name(placeholder='name') }}
{{ form.surname(placeholder='surname') }}
{{ form.email(placeholder='email') }}
{{ form.password(placeholder='password') }}
{{ form.confirm_password(placeholder='confirm password') }}
<input type="submit" value="Register">
</form>
<p>Already registered? Log in here</p>
</div>
<div class="error-area">
{% for error in form.confirm_password.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
</div>
class
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import InputRequired, Length, EqualTo
class RegisterForm(FlaskForm):
name = StringField('name', validators=[InputRequired()])
surname = StringField('surname', validators=[InputRequired()])
email = StringField('email', validators=[InputRequired()])
password = PasswordField('password', validators=[InputRequired(), Length(min=6)])
confirm_password = PasswordField('confirm passord', validators=[InputRequired(), Length(min=6), EqualTo(password)])
flask
#app.route('/register')
def register():
#declare a register form
form = RegisterForm()
#validate form
if form.validate_on_submit():
print('validate')
return '<h1>Success</h1>'
else:
print('not validated')
print(form.errors)
return render_template('register.html', form=form)
The problem with my code is that validation seems not to be working. Even if I fill the form with the "valid" input, form.validate_on_submit() always fail.
What I can't understand is that even when I try to print array errors, no error shows.
What am I missing?
There are a few issues here. Firstly, in your html, you haven't set a method attribute for the form. This means that it defaults to GET, which is why the form isn't validating. This can be changed like so:
<form action="{{ url_for('register') }}" method='POST'>
Incidentally, as the view that loads the form is the same as the target, you can leave out the action attribute, giving us:
<form method='POST'>
Secondly, in your class, you have a couple of issues with the confirm_password field. Firstly, you have a typo in PasswordField('confirm passord'. Secondly, the EqualTo() validator expects a string, not a field. We need to change this line in full to:
confirm_password = PasswordField('confirm password', validators=[InputRequired(), Length(min=6), EqualTo('password')])
Finally, in your flask, we need to accept POST requests to the view. This can be done by altering #app.route():
#app.route('/register', methods=['POST', 'GET'])
Having made these changes, the form should work as expected.
I have a registration form created by the class:
class RegistrationForm (Form):
username = StringField('username', validators = [DataRequired()])
and The model:
class User (db.Model):
UserName = db.Column(db.String(25),nullable=False, unique=True)
I'd like to check if the field for username for duplicates in the server and then return an error of that field ... is it possible without using flash messages ?
since I'm using a macro formhelper to show field errors next to the field which contains errors..
or is there a way to specify that this field is unique in the Validators ?
Thanks:
The macro:
{% macro render_field(field) %}
{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul>
{% for error in field.errors %}
<li style="color:red">{{ error }}</li>
{% endfor %}
<li style="color:red">{{ error_message }}</li>
</ul>
{% endif %}
{% endmacro %}
Define a method "validate_<name of field>" on the form. This will run whatever custom validation you want. In this case, check if the username is already used and raise an error if it is.
from flask_wtf import Form
from wtforms.fields import StringField
from wtforms.validators import ValidationError, InputRequired
class RegistrationForm(Form):
username = StringField('username', validators=[InputRequired()])
def validate_username(self, field):
# count the number of user ids for that username
# if it's not 0, there's a user with that username already
if db.session.query(db.func.count(User.id)).filter_by(username=field.data).scalar():
raise ValidationError('this username is already taken')
As an optimization, this counts the id field rather than actually fetching or counting the entire model.
See the WTForms docs for more information.
Note that some people consider this a security issue because an attacker could determine if a name is registered by trying them here and seeing if it fails.