I'm making a registration form using Flask-wtforms and sqlite as my database but when I try to submit it does not push to the database.
I have checked whether i had not put a method of 'post'in my registration form and also whether my db had been created when i configured it.
This my forms.py
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
from flask_login import current_user
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from app.models import User
from app import db
class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
picture=FileField('Profile picture',validators=[DataRequired(),FileAllowed(['jpg', 'png'])])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('That username is taken. Please choose a different one.')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('That email is taken. Please choose a different one.')
Register.html
<form action="" method="POST" class='register-form'>
{{ form.hidden_tag() }}
<div class=''>
{{form.picture.label}}
{{form.picture}}
{%if error in form.picture.errors%}
<span style="color: red;">[{{ error }}]</span>
{% endif %}
</div>
<p >
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.email.label }}<br>
{{ form.email(size=32) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.confirm_password.label }}<br>
{{ form.confirm_password(size=32) }}<br>
{% for error in form.confirm_password.errors %}
<span style="color: red;"> [{{"Typo! Passwords didn't match"}}]</span>
{% endfor %}
</p>
<p>{{ form.submit(class="btn btn-primary") }}</p>
</form>
Where i have configured my sqlite db
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hidden secret key8e9808798709809'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
db = SQLAlchemy(app)
manager = Manager(app)
No error message i just know that it is not pushing new users to the db.
Got it!
Hope this helps someone out there ..lol...
adding to the register.html
<form method="POST" enctype="multipart/form-data">
This way the request.files won't be empty
Related
I'm using Python with Flask with WTForms
I have two forms that are virtually identical called, login and register. However, the login form retains the email address after submission where register does not.
Troubleshooting:
I've compare the source code for both forms and I'm not finding any obvious difference.
I've searched online for potential solutions but everything looks like what I have coded.
I'm not seeing a reason why login works but register does not.
What am I missing? How do I get the field value to reload after submission on the registration page?
Login View:
#pages.route("/login", methods=["GET", "POST"])
def login():
if session.get("email"):
return redirect(url_for(".index"))
form = LoginForm()
if form.validate_on_submit():
user_data = current_app.db.user.find_one({"email": form.email.data})
if not user_data:
flash("Login credentials not correct", category="danger")
# print(form.email.data)
return redirect(url_for(".login"))
user = User(**user_data)
if user and pbkdf2_sha256.verify(form.password.data, user.password):
session["user_id"] = user._id
session["email"] = user.email
return redirect(url_for(".index"))
flash("Login credentials are incorrect", category="danger")
return render_template("login.html", title="Login", form=form)
Register View:
#pages.route("/register", methods=["GET", "POST"])
def register():
if session.get("email"):
return redirect(url_for(".index"))
form = RegisterForm()
if form.validate_on_submit():
#-------------------------------------------------
user_data = current_app.db.user.find_one({"email": form.email.data})
if user_data:
flash("Account already exists", category="danger")
print("In /register - Account already exists")
return redirect(url_for(".register"))
#--------------------------------------------------
user = User(
_id=uuid.uuid4().hex,
email=form.email.data,
password=pbkdf2_sha256.hash(form.password.data),
)
current_app.db.user.insert_one(asdict(user))
flash("User registered successfully", "success")
return redirect(url_for(".login"))
return render_template("register.html", title="Register", form=form)
forms.py
from flask_wtf import FlaskForm
from wtforms import (
IntegerField,
PasswordField,
StringField,
SubmitField,
TextAreaField,
URLField,
)
from wtforms.validators import (
InputRequired,
Email,
EqualTo,
Length,
NumberRange,
)
class LoginForm(FlaskForm):
email = StringField("Email", validators=[InputRequired(), Email()])
password = PasswordField("Password", validators=[InputRequired()])
submit = SubmitField("Login")
class RegisterForm(FlaskForm):
email = StringField("Email", validators=[InputRequired(), Email()])
password = PasswordField(
"Password",
validators=[
InputRequired(),
Length(
min=12,
message="Your password must be at least 12 characters long.",
),
],
)
submit = SubmitField("Register")
login.html snippet:
<form name="login" method="post" novalidate class="form">
{% with messages = get_flashed_messages(with_categories=true) %}
{%- for category, message in messages %}
<span class="form__flash form__flash--{{category}}"> {{ message }}</span>
{% endfor %}
{% endwith %}
<div class="form__container">
{{ form.hidden_tag() }}
{{ render_text_field(form.email) }}
{{ render_text_field(form.password) }}
<div>
{{ form.submit(class_="button button--form") }}
</div>
<div class="page-link">
Forgot password?
</div>
</div>
<section class="login-footer">
<div class="page-link">
Don't have an account? Register now
</div>
</section>
</form>
register.html snippet:
<form name="register" method="post" novalidate class="form">
{% with messages = get_flashed_messages(with_categories=true) %}
{%- for category, message in messages %}
<span class="form__flash form__flash--{{category}}"> {{ message }}</span>
{% endfor %}
{% endwith %}
<div class="form__container">
{{ form.hidden_tag() }}
{{ render_text_field(form.email) }}
{{ render_text_field(form.password) }}
<div>
{{ form.submit(class_="button button--form") }}
</div>
</div>
<section class="registration-footer">
<div class="page-link">
Already have an account? Log in here
</div>
</section>
</form>
I'm following a web app tutorial using Flask, and it has a section on how to implement WTForms. I followed everything correctly, it all works fine like I can go onto the page and all the fields are showing. But I cannot type anything into the actual fields. The button works and it brings up the error page since I haven't done anything to store them, but I physically cant click on for example username and start typing. It treats it like its a bunch of text. The tutorial I'm following is two years old, so that probably is the problem and maybe a new update has changed it.
My current version of WTForms is 2.3.3
I've tried to look at the documentation and also follow the latest tutorials, but still no progress. I checked out the crash course section and still anything I tried to edit to match that style just doesn't fix it.
Heres how it looks:
forms.py:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember = BooleanField('Remember Me')
submit = SubmitField('Login')
Register.html:
{% extends "layout.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ form.username.label(class="form-control-label") }}
{{ form.username.label(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.email.label(class="form-control-label") }}
{{ form.email.label(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.password.label(class="form-control-label") }}
{{ form.password.label(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-control-label") }}
{{ form.confirm_password.label(class="form-control form-control-lg") }}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
<div class="border-top pt-3">
<small class="text-muted">
Already Have An Account? <a class="ml-2" href="{{ url_for('login') }}">Login</a>
</small>
</div>
{% endblock content %}
Main.py:
from flask import Flask, render_template, url_for
from forms import RegistrationForm, LoginForm
app = Flask(__name__)
posts = [
{
"author": "John Appleseed",
"title": "Blog Post 2",
"content": "Second Post Content",
"date_posted": "Feb 26 2021"
},
{
"author": "John Doe",
"title": "Blog Post 3",
"content": "Third Post Content",
"date_posted": "Feb 27 2021"
}
]
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html', posts=posts)
#app.route('/about')
def about():
return render_template('about.html', title="About")
#app.route('/register')
def register():
form = RegistrationForm()
return render_template('register.html', title="Register", form=form)
I simply had to remove the .label in my HTML file.
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ form.username.label(class="form-control-label") }}
{{ form.username(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.email.label(class="form-control-label") }}
{{ form.email(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.password.label(class="form-control-label") }}
{{ form.password(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-control-label") }}
{{ form.confirm_password(class="form-control form-control-lg") }}
</div>
</fieldset>
<div class="form-group">
I am trying to use flask-wtf form validators on some vaiables. All the examples for form validations uses actual form to do the validation. I want to use the validators on few variables without a form. Below are the snippets. I tried this way but it's of no use. I am getting False on form.validate() and I am getting [] lists for errors. Please guide me with the right process.
#this is my form_validations.py
#########################################################################################################
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email, Length
class SignUpForm(FlaskForm):
username = StringField('username', validators=[DataRequired(), Length(min=1, max=50)])
email = StringField('email', validators=[DataRequired(), Email()])
mobile = StringField('mobile', validators=[DataRequired(), Length(min=10, max=15)])
#########################################################################################################
#this is the function where I want to use the form validations.
def userSignUp():
username = request.form['username']
email = request.form['email']
mobile = request.form['mobile']
form = {
"username" : str(username),
"email": str(email),
"mobile": str(mobile)
}
formData = MultiDict(mapping=form)
print(formData)
form = SignUpForm(formData)
if form.validate():
output = {"result": "success"}
else:
output = {"result": "failed"}
# print(form.validate(),form.username.errors,form.email.errors,form.mobile.errors)
return jsonify(output)
Here is a simplified example.
The route:
#app.route("/signup", methods=["GET", "POST"])
def signup():
form = SignUpForm()
if form.validate_on_submit():
app.logger.info(f'Form validated - data: {form.data}')
# redirect somewhere upon success...
# return redirect('/somewhere')
return render_template("signup.html", form=form)
The class code:
class SignUpForm(FlaskForm):
username = StringField('username', validators=[validators.DataRequired(), validators.Length(min=1, max=50)])
email = StringField('email', validators=[validators.DataRequired(), validators.Email()])
mobile = StringField('mobile', validators=[validators.DataRequired(), validators.Length(min=10, max=15)])
Slight modification on my end (the validators), because I import like this:
from wtforms import (
Form, validators, widgets, FieldList,
BooleanField, TextField, IntegerField, SelectField, SelectMultipleField,
TextAreaField, StringField, SubmitField
)
The template:
{% extends "base.html" %}
{% block title %}Sign up{% endblock %}
{% block subtitle %}Sign up{% endblock %}
{% block content %}
<h1>Sign up page</h1>
{% if form.is_submitted() and form.errors|count > 0 %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ form.errors|count }} error(s):
<ul>
{% for field_name, field_errors in form.errors|dictsort if field_errors %}
{% for error in field_errors %}
<li>{{ form[field_name].label }}: {{ error }}</li>
{% endfor %}
{% endfor %}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<div class="row">
<div class="col">
<form class="row g-3" method="post" action="{{ url_for('signup') }}" class="form-inline">
{{ form.csrf_token }}
<div class="mb-3 col-md-4">
<label for="username" class="form-label">Username</label>
{{ form.username(class="form-control") }}
</div>
<div class="mb-3 col-md-4">
<label for="email" class="form-label">Email</label>
{{ form.email(class="form-control") }}
</div>
<div class="mb-3 col-md-4">
<label for="mobile" class="form-label">Mobile</label>
{{ form.mobile(class="form-control") }}
</div>
<div class="mb-3">
<button type="submit" id="save" name="save" class="btn btn-success" value="save">Save</button>
</div>
</form>
</div>
</div>
{% endblock %}
I am using Bootstrap, so you can see I am letting Flask_wtf automatically generate the form fields but apply certain classes. However, I have added a submit button manually but you include it in your form as an optional field. Also you can add all the form fields yourself, then make sure they keep their original names. NB: I added the hidden CRSF field too (populated automatically).
The key is to use validate_on_submit to force validation of the form and populate the list of errors. As you can see, the template can iterate on the list of errors and display them as list items.
Here is what the form looks like:
Edit, Solved: The validate() method which validate_on_submit() calls requires the values for SelectField options to be integers. Adding the coerce=int option to the SelectField solved the problem. The field now looks like this:
time_zone = SelectField('Time Zone', choices=[(-1, '--Select Time Zone--'),(0, 'PST'), (1, 'MST'), (2, 'EST')], coerce=int)
Original Post:
I am working on a registration form using WTForms (v2.2.1) in Flask(v1.1.1). When I have a SelectField in the form class form.validate_on_submit() automatically returns False
I followed the pattern in the documentation and looked through most of the "Similar questions" SO suggests to no avail. Many of the other posts are missing {{ form.csrf_token }} or {{ form.hidden_tag() }} below the form tag. I've tried both with the same outcome.
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectField
from wtforms.validators import DataRequired, Email, EqualTo, ValidationError
class RegisterForm(FlaskForm):
first_name = StringField('First Name', validators=[DataRequired()])
last_name = StringField('Last Name', validators=[DataRequired()])
email = StringField('Email Address', validators=[DataRequired(), Email()])
phone = StringField('Phone Number', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
time_zone = SelectField('Time Zone', choices=[(-1, '--Select Time Zone--'),(0, 'PST'), (1, 'MST'), (2, 'EST')])
submit = SubmitField('Register')
index.html (The form is rendered on the index.html page and processed in the /register route)
{% extends 'base.html' %}
{% block content %}
<div class="flex-item p-5 register">
<h2>Register</h2>
<form action="{{ url_for('register') }}" method="post" novalidate>
{{ register_form.hidden_tag() }}
<div class="form-row">
<div class="form-group col-md-6">
{{ register_form.first_name.label }}
{{ register_form.first_name(class="form-control") }}
</div>
<div class="form-group col-md-6">
{{ register_form.last_name.label }}
{{ register_form.last_name(class="form-control") }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
{{ register_form.email.label }}
{{ register_form.email(class="form-control") }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
{{ register_form.phone.label }}
{{ register_form.phone(class="form-control") }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
{{ register_form.password.label }}
{{ register_form.password(class="form-control") }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
{{ register_form.confirm_password.label }}
{{ register_form.confirm_password(class="form-control") }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
{{ register_form.time_zone.label }}
{{ register_form.time_zone(class="form-control") }}
</div>
</div>
{{ register_form.submit(class="btn btn-primary") }}
</form>
</div>
{% endblock %}
routes.py
from flask import render_template, redirect, url_for, request, flash
from app import app
from app.forms import RegisterForm
#app.route('/', methods=['GET', 'POST'])
#app.route('/index', methods=['GET', 'POST'])
def index():
return render_template('index.html', register_form=RegisterForm())
#app.route('/register', methods=['POST'])
def register():
form = RegisterForm()
print(form.is_submitted(), form.validate())
# This is printing (True, False) when there is a SelectField
# and (True, True) when no SelectField is in the RegisterForm Class
if form.validate_on_submit():
print('validating register form')
return redirect(url_for('index'))
If I remove the SelectField from the RegisterForm class, validate_on_submit() works fine.
Any thoughts on what is causing this issue?
I'm trying to read a form using Flask, WTForms and the macros that are made available via flask-Bootstrap. This works in some cases and in other - seemingly similar ones - it doesn't. When I check the content of a specific StringField it always evaluates to None, failing the InputRequired() validation.
forms.py:
from wtforms import Form, validators, \
StringField, PasswordField, SubmitField, \
SelectField, RadioField
from wtforms.fields.html5 import DateField
from wtforms.validators import ValidationError, InputRequired, NoneOf, \
EqualTo, Length, Optional
class TestForm(Form):
name = StringField('Name', [validators.InputRequired(message='Please enter name!')])
date = DateField('Date', format='%Y-%m-%d') #, default=date.today())
address = StringField('Address')
submit = SubmitField('Add')
test.py:
from flask import render_template, redirect, request
from flask_security import login_required
from application.routes import routes
from application.forms import TestForm
#routes.route('/test', methods=['POST', 'GET'])
#login_required
def test():
form = TestForm()
print(form.name.data)
if request.method == 'POST' and form.validate():
print("validated")
return redirect("/")
return render_template('test.html', title="Asset Manager", form=form)
test.html:
{% extends 'layout.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
<div class="container-fluid">
<h3><u>Test:</u></h3>
<br>
<div class="container-fluid col-md-3">
<form action="" method="POST" class="form" role="form" onsubmit="/">
<div class="row">
<div class="col-md-12">
{{ wtf.form_field(form.name, form_type="inline", placeholder="Name") }}
</div>
</div>
<div class="row">
<div class="col-md-5">
{{ wtf.form_field(form.date) }}
</div>
</div>
<div class="row">
<div class="col-md-12">
{{ wtf.form_field(form.address) }}
</div>
</div>
{{ wtf.form_field(form.submit, class="btn btn-primary") }}
</form>
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endblock %}
This always puts out None and consequently never gets past the validation check even when I enter a name and press the Add button.
I tried creating a minimal example (which still exhibits the same problem), but I left out large parts of the application, so if you need more information - please let me know and I'll be happy to provide them.
Thanks to Marc I found where I missed something. When instantiating the form I need to pass the request as an argument.
So the line
form = TestForm()
needs to be changed to
form = TestForm(request.form)
To make the application work.