I have a Flask-WTF form for sign in. Apparently the form is never valid, no matter what I enter "success" is never printed. Why isn't my form validating?
class loginForm(Form):
email = EmailField('email', validators=[InputRequired("Please enter your email address."), Email("Please enter a valid email address.")])
password = PasswordField('password', validators=[InputRequired("Please enter your password.")])
#app.route('/sign-in', methods=['POST', 'GET'])
def signIn():
form = loginForm(request.form)
if form.validate_on_submit():
print 'success'
return redirect('/')
return render_template('signIn.html')
<form method="POST" action="/sign-in">
{{ form.email(placeholder='Email', class="textBox") }}
{{ form.password(placeholder='Password', class="textBox") }}
<button onclick="submit()">Sign In</button>
</form>
Flask-WTF adds a CSRF protection field. If it's not present, the CSRF validation will fail, and the form will be invalid. Use form.hidden_tag() to include any hidden fields in your form (including the CSRF field).
<form method="post">
{{ form.hidden_tag() }}
...
In general, if a form is not validating, you should check form.errors after calling validate to see what's wrong.
You don't see the error since you're not rendering that field (or rendering the errors for any fields in this case, but that wouldn't help with this issue). If you ran in a debugger and examined form.errors, you would see that there was indeed a "CSRF token missing" error.
Related
I have a Flask-WTF form for sign in. Apparently the form is never valid, no matter what I enter "success" is never printed. Why isn't my form validating?
class loginForm(Form):
email = EmailField('email', validators=[InputRequired("Please enter your email address."), Email("Please enter a valid email address.")])
password = PasswordField('password', validators=[InputRequired("Please enter your password.")])
#app.route('/sign-in', methods=['POST', 'GET'])
def signIn():
form = loginForm(request.form)
if form.validate_on_submit():
print 'success'
return redirect('/')
return render_template('signIn.html')
<form method="POST" action="/sign-in">
{{ form.email(placeholder='Email', class="textBox") }}
{{ form.password(placeholder='Password', class="textBox") }}
<button onclick="submit()">Sign In</button>
</form>
Flask-WTF adds a CSRF protection field. If it's not present, the CSRF validation will fail, and the form will be invalid. Use form.hidden_tag() to include any hidden fields in your form (including the CSRF field).
<form method="post">
{{ form.hidden_tag() }}
...
In general, if a form is not validating, you should check form.errors after calling validate to see what's wrong.
You don't see the error since you're not rendering that field (or rendering the errors for any fields in this case, but that wouldn't help with this issue). If you ran in a debugger and examined form.errors, you would see that there was indeed a "CSRF token missing" error.
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 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.
My problem is: when an user refresh a form, the data in the Form is sent.
I have a Form with a POST request.
The user writes his name, mail and a message. If the mail is correct, the message is sent.
In my view, if the Form is valid, I add the message in my model Message.
After that I disable the "Send" button. But if the user refreshes the page, my view is called, and another row is added in my model.
I would like, when the user refreshes the page, to block the POST.
My View:
def contact(request):
form = MessageForm(request.POST or None)
if form.is_valid():
name = form.cleaned_data['name']
message = form.cleaned_data['message']
mail = form.cleaned_data['mail']
new_message = Message()
new_message.name = name
new_message.message = message
new_message.mail = mail
new_message.save()
envoi = True
return render(request, 'vautmieux/contact.html', locals())
My URL:
path('contact/', views.contact, name='contact'),
My HTML:
<form action="{% url "contact" %}" method="post">
{% csrf_token %}
<div class="row">
<div class="col-md-6">
{{ form.name }}
{{ form.mail }}
</div>
<div class="col-md-6" >
{{ form.message }}
</div>
<button id="sendMessageButton" type="submit">ENVOYER LE MESSAGE !</button>
</div>
{% if envoi %}Votre message a bien été envoyé !{% endif %}
</form>
This is the main reason why people implement the Post/Redirect/Get pattern [wiki]. In case of a successful POST request, you should return a redirect to a URL. As a result the browser will perform a GET, and in case the browser thus performs a refresh later, it will make a GET again.
def contact(request):
if request.method == 'POST':
form = MessageForm(request.POST)
if form.is_valid():
form.save()
return redirect('some-message-successful-view')
else:
form = MessageForm()
return render(request, 'vautmieux/contact.html', {'form': form})
Here 'some-message-successful-view' needs to be replaced with the name of a view you trigger when sending a message was succesful. This can be the same view as the one defined here. I advice to use Django's message framework [Django-doc] to send a message to the user that the message has been submitted successfully.
I'm making a register page for my django (version 2.0) website, but it pretty messy to me, I'm pretty sure the bullet points and the additional information is not supposed to show up right away.
How can I make this register page look cleaner? Ie. just the username, password, and confirmation textbox, rather than all the messages.
Thanks!
Register.html
<h2>Sign up</h2>
<br>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Sign up</button>
</form>
Register view
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
login(request, user)
return redirect('index')
else:
form = UserCreationForm()
return render(request, 'todo/register.html', {'form': form})
There are many ways to achieve this.
Overriding the default UserCreationForm()
class MyForm(UserCreationForm):
email = forms.EmailField(required=True)
email.help_text = ''
...
In the view template instead of using {{ form.as_p }} render the form manually
<form action="" method="post">{% csrf_token %}
{{ form.username.label }}
{{ form.username }}
...
</form>
Design your custom template and map the field name to the form field name.
Hope it helps !
You did not specify where this UserCreationForm came from nor your django version, but anyway: searching django's code source it appears that those are the help_text for the various widgets / fields of the default contrib.auth.forms.UserCreationForm, so yes the "bullet points and the additional information" is actually "supposed to show up right away". And as far as I'm concerned (from a user perspective I mean) it's a good thing that it does "show up right away", so I don't have to retype username and passwords twice, thrice or more until I found out by trial/errors what the system expects (or, more often, just plain give up registering on this site).
Now if you really want to frustrate your users (and loose half of them on the way), you can of course mask all those useful informations by rendering the form manually so you have full control on which messages appear, when, where and how.