validate_on_submit() not working in Flask. What should I do? - python

I am new to Flask.
The validate_on_submit() is not working and I also don't know what app.app_context() and app.test_request_context() do in my code.
The only thing I am trying to do is get my form to validate I can't figure out why it's not working.
This is my main.py
from flask import Flask, render_template, request, flash
from the_first_wt_form import MyForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '934kegn298u54kjgnjdkrbg9u939'
with app.app_context():
with app.test_request_context():
a_form = MyForm()
#app.route('/', methods=['GET', 'POST'])
def home():
if request.method == "POST":
name = request.form['name']
print(name)
email = request.form['email']
print(email)
passwrd = request.form['password']
print(passwrd)
con = request.form['confirm']
print(con)
if a_form.validate_on_submit():
print("Good job")
name = request.name.data
print(name)
else:
print('We messed up')
if a_form.errors != {}:
for err in a_form.errors.values():
print(f"There was an error with creating user: {err}")
flash(f"There was an error with creating user: {err}", category='danger')
return render_template('mynewhome.html', form=a_form)
if __name__ == "__main__":
app.run(debug=True)
This is the code from My wt_form.py
from wtforms import StringField, PasswordField, validators, SubmitField
from flask_wtf import FlaskForm
class MyForm(FlaskForm):
name = StringField('name', [validators.Length(min=4, max=25), validators.DataRequired()])
email = StringField('Email Address', [validators.Length(min=6, max=35), validators.Email()])
password = PasswordField('New Password', [
validators.DataRequired(),
validators.EqualTo('confirm', message='Passwords must match')
])
confirm = PasswordField('Repeat Password')
submit = SubmitField('Register')
And Finally this is mynewhome.html
<!DOCTYPE html> <html lang="en"> <head>
<meta charset="UTF-8">
<title>How are you?</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
</head> <body>
<h1> Hello BRo </h1> <br><br> {% with messages = get_flashed_messages(with_categories = true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert--{{ category }}">
<button type="button" class="m1-2 mb-1 close" data-dismiss="alert" aria-label="Close">
{{ message }}
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %} <br><br>
<div class="container">
<form method="POST" action="/" class="form-register">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(class = "form-control", Placeholder = "Usern Name") }}
{{ form.email.label }} {{ form.email(class = "form-control", Placeholder = "Email Address") }}
{{ form.password.label }} {{ form.password(class = "form-control", Placeholder = "Password") }}
{{ form.confirm.label }} {{ form.confirm(class = "form-control", Placeholder = "Confirm Password") }}
<br>
{{ form.submit(class = "btn btn-lg btn-block btn-primary") }}
</form> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script> </body> </html>

As a new flask user and simple flask usage, you should not need app_context and test_request_context. You can have a look on the documentation to understand them, but you don't need them in this situation.
You have to instantiate your form in the view function home
It is also a better practice to use form data only after validation, as you never know what the user enter in your form.
In the import you were importing the_first_wt_form but you said your file is called wt_form so I made the appropriate changes. But accordingly to your modules setup it might be wrong.
The main.py should look like (I tested it):
from flask import Flask, render_template, request, flash
from wt_form import MyForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '934kegn298u54kjgnjdkrbg9u939'
#app.route('/', methods=['GET', 'POST'])
def home():
a_form = MyForm()
if request.method == "POST":
if a_form.validate_on_submit():
print("Good job")
name = a_form.name.data
print(name)
# (...)
else:
print('We messed up')
if a_form.errors != {}:
for err in a_form.errors.values():
print(f"There was an error with creating user: {err}")
flash(f"There was an error with creating user: {err}", category='danger')
return render_template('mynewhome.html', form=a_form)
if __name__ == "__main__":
app.run(debug=True)
Note that you can access data from the a_form instance directly rather than on the request instance.

Related

Jinja2 wtform checkbox is check

Good day
I'm struggling to find a way to check in jinja2 if a wtform check box is checked.
I want to be able to make an if statement that shows when a check box is check that it will display additional input fields like:
{% if form.BooleanField == checked %}
display fields
{% endif %}
To enable one or more form fields you can use the disabled attribute of an input field or field set. Using a style sheet rule, the respective element is displayed or not.
When the user clicks the checkbox, an event listener updates the disabled attribute based on the state of the box.
An additional validator is required to validate the optional fields on the server side as well. This expects an entry in the respective field as long as the value of the referenced form field has been set.
Below is the complete code for a sample application.
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm
from wtforms import (
BooleanField,
StringField,
SubmitField
)
from wtforms.validators import (
DataRequired,
Optional
)
app = Flask(__name__)
app.secret_key = 'your secret here'
class RequiredIf(DataRequired):
field_flags = ('requiredif',)
def __init__(self, other_field_name, message=None, *args, **kwargs):
self.other_field_name = other_field_name
self.message = message
def __call__(self, form, field):
other_field = form[self.other_field_name]
if other_field is None:
raise Exception('no field named "%s" in form' % self.other_field_name)
if bool(other_field.data):
super(RequiredIf, self).__call__(form, field)
class ExampleForm(FlaskForm):
enabled = BooleanField(validators=[Optional()])
field1 = StringField(validators=[RequiredIf('enabled')])
field2 = StringField(validators=[RequiredIf('enabled')])
submit = SubmitField()
#app.route('/', methods=['GET', 'POST'])
def index():
form = ExampleForm(request.form)
if form.validate_on_submit():
for f in form:
print(f'{f.name}: {f.data}')
return render_template('index.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
<style>
fieldset[disabled], input[disabled] {
display: none;
}
</style>
</head>
<body>
<form method="post">
{{ form.csrf_token }}
<div>
{{ form.enabled }}
{{ form.enabled.label() }}
</div>
<fieldset name="optional-fields" {% if not form.enabled.data %}disabled{% endif %}>
<div>
{{ form.field1.label() }}
{{ form.field1 }}
</div>
<div>
{{ form.field2.label() }}
{{ form.field2 }}
</div>
</fieldset>
{{ form.submit }}
</form>
<script type="text/javascript">
(function() {
const enabledField = document.getElementById('enabled');
const optionalFieldset = document.querySelector('fieldset[name="optional-fields"]');
enabledField.addEventListener('change', function(event) {
optionalFieldset.disabled = !this.checked;
});
})();
</script>
</body>
</html>

Why input validators IPAddress, Length do not provide user any error message, but just prevent him from submitting button?

Need advice regarding WTForms Flask: I need to use ip address validator and maximum length validator (IPAddress, Length) - they works, but do not give user any error messages, on the other hand InputRequired validator works fine. I checked documentation and have no idea what could be the problem with the code.
// app.py file:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import InputRequired, IPAddress, Length
app = Flask(__name__)
app.secret_key = "test"
StringField()
class MyForm(FlaskForm):
inp_required_str = StringField("Input required: ", validators=
[InputRequired()])
max_len_str = StringField("Max length < 5: ", validators=
[InputRequired(), Length(max=5, message="Less than 5!")])
ip_address_str = StringField("Is ip address: ", validators=
[InputRequired(), IPAddress(message="Should be ip!")])
button = SubmitField("Click me!")
#app.route('/', methods=["GET", "POST"])
def hello_world():
form = MyForm()
if form.validate_on_submit():
# do some work here
return render_template("test.html", form=form, message="Fine?")
return render_template("test.html", form=form)
if __name__ == '__main__':
app.run()
// html template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<form method="post">
{{ form.hidden_tag() }}
{{form.inp_required_str.label}} {{form.inp_required_str}} <br>
<br>
{{form.max_len_str.label}} {{form.max_len_str}} <br> <br>
{{form.ip_address_str.label}} {{form.ip_address_str}} <br> <br>
{{form.button}} <br> <br>
</form>
<h1>{{ message }}</h1>
</body>
</html>
You are not rendering the error messages that get returned by calling validate_on_submit. You need to add some kind of logic to do that for you. InputRequired validator works fine because Wtforms adds an required attribute to your input field and that is managed by the browser itself.
I would suggest you use a macro for that as stated here:
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}

Raising an error in WTForm using jinja2

I'm trying to raise an error in Jinja2, in a WTForm, the error should be raised if url input is not validated, but when i submit an invalide url, i get a popup saying "Please enter a url".
how do i pass the default popup and add a custom error message ?
here is the main py:
from datetime import datetime
from flask import Flask, render_template, url_for, request, redirect,flash
from logging import DEBUG
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from flask.ext.wtf.html5 import URLField
from wtforms.validators import DataRequired , url
app = Flask(__name__)
app.logger.setLevel(DEBUG)
app.config['SECRET_KEY']='{#\x8d\x90\xbf\x89n\x06%`I\xfa(d\xc2\x0e\xfa\xb7>\x81?\x86\x7f\x1e'
#app.route('/')
#app.route('/index')
def index():
return render_template('base.html')
#app.route('/add', methods=['GET','POST'])
def add():
return render_template('add.html')
# HERE IS THE LOGIN FORM
class Login(FlaskForm):
username = StringField('username')
password = PasswordField('password')
url = URLField('url', validators=[DataRequired(),url()])
#app.route('/form', methods=['GET','POST'])
def form():
form = Login()
if form.validate_on_submit():
url = form.url.data
return redirect(url_for('index'))
return render_template('form.html',form = form )
if __name__ =='__main__':
app.run(debug=True)
and here is the template:
<!DOCTYPE html>
<html>
<head>
<title>form</title>
</head>
<body>
<h1>Hello !</h1>
<form method="POST" action="{{url_for('form')}}">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}
{{ form.password.label }}
{{ form.password }}
{{ form.url.label }}
{{ form.url }}
{% if form.url.errors %} <p> {{error}}</p> {% endif %}
<button type="submit">Submit</button>
</form>
</body>
</html>
Because you're using the data type URLField, this is rendered as a HTML5 "url" form field type.
Your browser recognises this and performs its own validation on the data submitted:
There is no way for you to override this - it's built in to the browser.
If you need to show a custom error message, you might be able to use a TextField instead, and provide your own URL validation logic.
Add your own message instead of default message in your form defination.
url = URLField('url', validators=[DataRequired(),url(message="Please enter a valid url (e.g.-http://example.com/)")])
As Matt Healy before mentiones, it is the browser that validates URLField.
So if you want a custom error message use StringField (TextField is outdated). If required, a custom message can be used as shown below message='text to display'.
Example:
class XYZForm(FlaskForm):
url = StringField('url', validators=[DataRequired(),url(message='Please enter valid URL')])
description = StringField('description')
Of course the *.html should include code to output an error to the page:
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
It seems like novalidate attribute works for your case.

Form Validation message not being displayed - Flask

I'm having trouble getting error messages in Flask to render.
I suspect this is related to the blueprints. Previously, the logic seen in views.py was in the users blueprint, but I've since ported it over to the main blueprint. Anyhow, since then, I am unable to get error messages to render.
The specific line I think I'm having trouble with is:
self.email.errors.append("This Email is already registered")
project/main/views.py
#main_blueprint.route('/', methods=['GET', 'POST'])
#main_blueprint.route('/<referrer>', methods=['GET', 'POST'])
def home(referrer=None):
form = RegisterForm(request.form)
# prepares response
resp = make_response(render_template('main/index.html', form=form))
if form.validate_on_submit():
do_stuff()
return resp
project/main/index.html
<h1>Please Register</h1>
<br>
<form class="" role="form" method="post" action="">
{{ form.csrf_token }}
{{ form.email(placeholder="email") }}
<span class="error">
{% if form.email.errors %}
{% for error in form.email.errors %}
{{ error }}
{% endfor %}
{% endif %}
</span>
</p>
<button class="btn btn-success" type="submit">Register!</button>
<br><br>
<p>Already have an account? Sign in.</p>
</form>
project/user/forms.py
class RegisterForm(Form):
email = TextField(
'email',
validators=[DataRequired(), Email(message=None), Length(min=6, max=40)])
def validate(self):
print "validating"
initial_validation = super(RegisterForm, self).validate()
if not initial_validation:
print "not initial validation"
return False
user = User.query.filter_by(email=self.email.data).first()
print user
if user:
print self
print "error, email already registered"
self.email.errors.append("This Email is already registered")
return False
return True
When attempting to debug, the value for 'print user' from this is:
project.user.forms.RegisterForm object at 0x7fa436807698
Got it to work, #glls, you were correct.Rewrote the code as:
#main_blueprint.route('/', methods=['GET', 'POST'])
#main_blueprint.route('/<referrer>', methods=['GET', 'POST'])
def home(referrer=None):
# prepares response
resp = make_response(render_template('main/index.html', form=form))
if form.validate_on_submit():
do_stuff()
form = RegisterForm(request.form)
return resp

Why validation not working on form request?

I am learning flask and made a small application. Now I am trying to learn form. I used a simple code to validate a name request and it should give error when the field remains empty. But it isn't giving one.
Main file :
from flask import Flask, render_template
from flask.ext.moment import Moment
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField, validators
import requests
import json
from datetime import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'abcd'
moment = Moment(app)
class Nameform(Form):
name = StringField("whats your name?", [validators.Required()])
submit = SubmitField('submit')
#app.route('/')
#app.route('/index')
def index():
api_call = requests.get('https://api.stackexchange.com/2.2/users/moderators?order=desc&sort=reputation&site=stackoverflow') # api call to stack for user with highest scores in des order
var_1 = json.loads(api_call.text)
var_2 = [{'link': value['link'], 'name': value['display_name'], 'user_id': value['user_id']} for value in var_1['items']]
return render_template('index.html', posts=var_2, current_time=datetime.utcnow())
#app.route('/user/<id>/<user_name>')
def user(id, user_name):
print id
api_call = requests.get('https://api.stackexchange.com//2.2/users/'+id+'/reputation?site=stackoverflow') # api call for reputation of click user
var_1 = json.loads(api_call.text)
return render_template('reputation.html', result=var_1, user_name=user_name)
#app.route('/test', methods=['GET', 'POST'])
def user_form():
name = None
form = Nameform()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('test_form.html', form=form, name=name)
if __name__ == '__main__':
app.run(debug=True)
Template for rendering:
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
<form method=post>
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
Why it is not throwing any error? when the field remains empty
You can render the error messages using form.errors. Note that you're also missing your CSRF token, which is required for validation since you didn't disable WTF_CSRF_ENABLED, so I've added {{ form.csrf_token }}. See CSRF Protection.
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
{% for field in form.errors %}
{% for error in form.errors[field] %}
<div class="error">{{ error }}</div>
{% endfor %}
{% endfor %}
<form method=post>
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
I think you have not included the novalidate attribute with form.
The novalidate attribute is used to tell the web browser to not apply validation to the fields in this form, which effectively leaves this task to the Flask application running in the server.
For Sample code

Categories

Resources