Python Flask/WTForms Form Field Values Lost After Submit - python

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>

Related

NoReverseMatch at / Reverse for 'register_request' not found. 'register_request' is not a valid view function or pattern name

this is a shopping website my login and registration form is not working it is showing that no reverse path found error.
this is my navbar template
<div class="container py-5">
<p class="text-center">If you already have an account, <a href="{% url
'sweet:register_request' %}">login</a> instead.</p>
</div>
<div class="container py-5">
<p class="text-center">Don't have an account? Create an account.
</p>
</div>
login.html
this is my login template
{% extends "base.html" %}
{% load static %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{ login_form }}
<button class="btn btn-primary" type="submit">Login</button>
</form>
{% endblock %}
home.html
this is my registration template
{% extends "base.html" %}
{% load static %}
{% block content %}
<form method="POST">
<button class="btn btn-primary" type="submit">Register</button>
{% csrf_token %}
{{register_form}}
</form>
{% endblock %}
views.py
register_request is function defined to register the form and login_request is to login into my website
def register_request(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "Registration successful.")
return redirect('login_request')
messages.error(request, "Unsuccessful registration. Invalid information.")
form = NewUserForm()
return render(request,"home.html",{"register_form": form})
def login_request(request):
if request.method == "POST":
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
messages.info(request, "You are now logged in as {username}.")
return redirect("base.html")
else:
messages.error(request, "Invalid username or password.")
else:
messages.error(request, "Invalid username or password.")
form = AuthenticationForm()
return render(request, 'login.html',{"login_form": form})
urls.py
from django.contrib.auth.views import LogoutView, LoginView
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views
app_name="sweet"
urlpatterns = [
path('',views.allProdCat,name='allProdCat'),
path('<slug:c_slug>/',views.allProdCat,name='products_by_category'),
path('<slug:c_slug>/<slug:prod_slug>/', views.ProductDetail, name='ProdCatDetail'),
path('register/', views.register_request, name="register"),
path("login/", views.login_request, name="login"),
]
forms.py
the form is for registration and login
from django import forms
from django.contrib.auth.models import User
from . import models
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class NewUserForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
def save(self, commit=True):
user = super(NewUserForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
this are all my code please help
You are trying to get address of views.register_request in {% url 'sweet:register_request' %} but the name of url path you have chosen is different. You can do one of two options, in both you need to change name.
Option 1:
# from
path('register/', views.register_request, name="register"),
# to
path('register/', views.register_request, name="register_request"),
Option 2:
# from
{% url 'sweet:register_request' %}
# to
{% url 'sweet:register' %}
instead of this:
<div class="container py-5">
<p class="text-center">If you already have an account, <a href="{% url
'sweet:register_request' %}">login</a> instead.</p>
</div>
<div class="container py-5">
<p class="text-center">Don't have an account? Create an account.
</p>
</div>
Do this:
<div class="container py-5">
<p class="text-center">If you already have an account, <a href="{% url
'sweet:register' %}">login</a> instead.</p>
</div>
<div class="container py-5">
<p class="text-center">Don't have an account? Create an account.
</p>
</div>

Why error in Flask WTF "The CSRF session token is missing."?

I am writing an application on Flask, I made authorization on flask-login, but an error appears during authorization itself. I use also Flask-WTF.
I have the simplest form of Flask WTF:
class LoginForm(FlaskForm):
email = StringField("Email: ", validators=[Email()])
password = PasswordField("Password: ", validators=[DataRequired(), Length(min=4, max=100)])
submit = SubmitField("Войти")
There is an HTML form:
<form action="/accounting" method="POST">
{{ form.csrf_token }}
{{ form.email.label() }} {{ form.email() }}
{{ form.password.label() }} {{ form.password() }}
{{ form.submit() }}
</form>
I tried to specify the HTML form {{ form.hidden_tag() }}
There is also a function:
#app.route('/login', methods=['POST', 'GET'])
def login():
if current_user.is_authenticated:
return redirect('/accounting')
form = LoginForm()
if form.validate_on_submit():
email = request.form['email']
user = UserModel.query.filter_by(email=email).first()
if user is not None and user.check_password(request.form['password']):
login_user(user)
return redirect('/accounting')
return render_template('login.html', form=form)
I couldn't find an answer on the Internet that fits my case, so I'm asking here.
Why does an error appear when submitting the form?
Bad Request
The CSRF session token is missing.
After rendering, the token is present:
<input id="csrf_token" name="csrf_token" type="hidden" value="IjhlMDBjMDhmYTIwOGUyNGQ5OGNiMTY0ZGZhOTU3Njc0ZDJhNjY4MDgi.YSW1_g.fhKQUYljjLKqUtl0OdcuOgJx02U">
I tried it both ways:
app.config['SECRET_KEY'] = SECRET_KEY
app.secret_key = "ff82b98ef8727e388ea8bff063"
There are also such lines:
csrf = CSRFProtect(app)
csrf.init_app(app)
Your error is in the html template. remove the action and add the hidden tags
<form method="POST">
{{ form.hidden_tag() }}
{{ form.email.label() }} {{ form.email() }}
{{ form.password.label() }} {{ form.password() }}
{{ form.submit() }}
</form>

use flask-wtf form validations without an actual form

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:

WTForm validate_on_submit() returns False for selectField

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?

Why is my registration form not updating on submit

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

Categories

Resources