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.
Related
I am new to Flask and this practice exercise is really throwing me for a loop. I am trying to create a single page on LocalHost:5000. The Flask_Form has a single input dialogue for a name and I want it to export the name to a "names.txt" doc. From there I am looking for a way to authenticate whether a name is already in the "names.txt" doc.
from flask import Flask, render_template, flash
from flask_wtf import FlaskForm
import os
app = Flask(__name__)
# Secret Key
SECRET_KEY = os.urandom(32)
app.config['SECRET_KEY'] = SECRET_KEY
class NameForm(FlaskForm):
name = StringField("Name", validators=[DataRequired()])
name = None
#app.route('/')
def index()
form = NameForm()
render_template('naming.html', name=name)
After this I have a basic html doc
<!-- naming.html -->
{% for message in get_flashed_messages() %}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"
aria-label="Close"></button>
</div>
{% endfor %}
{% if name %}
<h1> Hello {{ name }}!</h1>
{% else %}
<h1>List of Users</h1>
<br/>
<form methods="POST">
{{ form.hidden_tag() }}
{{ form.name.label(class="form-label") }}
{{ form.name(class="form-control") }}
<br/>
{{ form.submit(class="btn btn-primary") }}
</form>
<br/><br/>
{% endif %}
Is there any way to perform what I am attempting to do? I have tried some personal research and haven't found anything fruitful.
I am in the process of designing a website for the Latin language. In the website, I have a feature called "Form Practice," where users can either enter a latin word OR choose a random verb, random adjective, random noun, random participle, or a random pronoun. You can see this layout of the feature in the image below:
Form Practice Feature
As you can see from the image, there are many fields that the user can input. I store all of this information in a Flask Form in a python file, as shown below:
from flask import render_template, request, json, Response, redirect, flash, url_for, session
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, HiddenField, RadioField
from wtforms.validators import DataRequired
class FormPracticeForm(FlaskForm):
form_name = HiddenField('Form Name')
LatinWord = StringField("Enter a word:", validators=[DataRequired()], id="LatinWord", render_kw={'onkeypress': 'handle(event)'} )
Macrons = RadioField(
u'Enter your choice:',
choices=[
('Yes', 'Yes'),
('No', 'No')],
validators=[DataRequired()])
AdjectiveRandomButton = SubmitField("Random Adjective")
NounRandomButton = SubmitField("Random Noun")
VerbRandomButton = SubmitField("Random Verb")
ParticipleRandomButton = SubmitField("Random Participle")
PronounRandomButton = SubmitField("Random Pronoun")
NextButton = SubmitField("Next", render_kw={'autofocus': True})
Then, in the actual HTML file, I reference the flask form and also handle potential errors:
{% extends "layout.html" %}
{% block content %}
<div class="container">
<form name="login" action="" method="post" novalidate>
<fieldset class="form-group">
{{ form.hidden_tag() }}
<div class="container">
<br>
<h2> Form Practice </h2> <br>
<div class="alert alert-info" role="alert">
This feature allows you to practice your word forms.
Please enter a word or select a random word. In addition, please specify whether or not you want to use macrons to get started!
<br>
<br>
NOTE: If you haven't learned a particular form yet, feel free to leave it blank - it will not be counted towards your score.
</div>
</div>
<div class="container">
<br>
<b> Use Macrons: </b>
<div class="form-group">
{% for subfield in form.Macrons %}
<div class="form-check">
{{ subfield }}
{{ subfield.label }}
</div>
{% endfor %}
{% for error in form.Macrons.errors %}
<span class="error-message">{{ error }}</span>
{% endfor %}
</div>
</div>
<div class="container">
<br>
<b> Enter a Latin word: <br> </b>
{{ form.LatinWord(size=35) }}
{% for error in form.LatinWord.errors %}
<span class="error-message"> {{ error }} </span>
{% endfor %}
<br>
<br>
<b>OR click one of the buttons to generate a random word</b>
</div>
<div class="row">
<div class="col">
{{ form.VerbRandomButton() }}
</div>
<div class="col">
{{ form.AdjectiveRandomButton() }} <br>
</div>
<div class="col">
{{ form.NounRandomButton() }}
</div>
<div class="col">
{{ form.ParticipleRandomButton() }}
</div>
<div class="col">
{{ form.PronounRandomButton() }}
</div>
</div>
<div class="container">
<br>
<br>
{{ form.NextButton() }}
</div>
<script>
function handle(e){
if(e.keyCode === 13){
e.preventDefault(); // Ensure it is only this code that rusn
}
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
</fieldset>
</form>
</div>
{% endblock %}
I am not necessarily running into any problems right now, but I am stuck. I want to make it so that when the user clicks on the "Random Noun" button, for example, a multi-select field catered to nouns appears below the button that the user can select. And then if the user wants to change their mind and wants to select a "Random Verb", the multi-select field that was there disappears and a new one appears under the "Random Verb" button and that is specialized to verbs. I am unsure how to do this using FlaskForms. I was considering switching AdjectiveRandomButton, NounRandomButton, VerbRandomButton, ParticipleRandomButton, and PronounRandomButton to a RadioField as a start, but am unsure where to go from there.
Any help would be appreciated, thanks so much!
Well you could try using if/else conditions but that would be very hard to do it and it might get deeply nested as you project grows. I would suggest to switch from flask forms to JavaScript + jQuery. Basically what I do is I set up the front-end using flask-wtf so it would send a SECRET_KEY to validate the request in the back-end when submitting a form and sending it using jQuery ajax call https://api.jquery.com/jQuery.post/. Then using JavaScript you can quite easy manipulate data-flow and it's presentation / appearance as you have all the access to UI document elements.
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 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.