unable to show custom error message in Jinja2 template - python

I'm writing a register user function in ( using Flask, Python, Jinja2) which i'm checking if a username (or email) is already present and if so shows an error to below TextField.
register code is:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = SignupForm()
error = None
if form.validate_on_submit():
user_by_name = Users.query.filter_by(username=form.username.data).first()
user_by_email = Users.query.filter_by(email=form.email.data).first()
if user_by_name:
error = 'Username already taken. Choose another'
return render_template('register.html', form=form, error = error)
elif user_by_email:
error = 'Email already registered. Login or register with another Email'
return render_template('register.html', form=form, error = error)
else:
#Add user details to DB logic
return redirect(url_for('index'))
return render_template('register.html', form=form, error = error)
I have a macro defined in a file util.html
{% macro render_field(field) %}
<div class="control-group {% if field.errors %}error{% endif %}">
{% if kwargs.get('label', True) %}
{{ field.label(class="control-label") }}
{% endif %}
<div class="controls">
{{ field(**kwargs) }}
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endmacro %}
and using this macro in register.html as:
{% from "util.html" import render_field %}
{% extends "base.html" %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{{ render_field(form.username, label=True, class="input-xlarge", autofocus="autofocus", errors=error) }}
{{ render_field(form.password, label=True, class="input-xlarge", autofocus="autofocus") }}
{{ render_field(form.confirm, label=True, class="input-xlarge", autofocus="autofocus") }}
{{ render_field(form.email, label=True, class="input-xlarge", autofocus="autofocus", errors=error) }}
<button class="btn" type="submit">Register</button>
</form>
{% endblock %}
Now when i test the localhost:5000/register with duplicate username or email address it doesn't show any error at all (also doesn't add any user into DB which is okay).
But when i enter any wrong email or leave any field blank it shows respective error, but not showing the error which i want to pass using register view.
Is there any Jinja2 related logic missing?
And why its showing errors related to blank field or wrong email but not which i try to pass on duplicate username etc.
Please suggest.

Well, you doing it wrong :)
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
This macro searches for error in respective field.errors
But you pass error context variable, which is not attached to the field.
The right way to solve this will be to add def validate_user to your form definition and check for everything in there.
If it goes wrong -> raise ValidationError(message)
See example here:
WTForms documentation
With this approach, your validate_on_submit() should not return True if it is not validated.

You can just flash the error like this
sample code
file: views.py
#app.route('/')
def index():
# some checks which provided error
if error:
flash(error)
return render_template('template.html')
file template.html
{% for category, msg in get_flashed_messages(with_categories=true) %}
<div class="category">{{ msg|safe }}</div>
{% endfor %}

Related

Flask flashed messages not showing

I'm trying to flash messages in Flask using flash(), but the messages aren't appearing on the page. It isn't in the source code for the page either when looking at it with Dev Tools.
Here is the code that I have in my base.html It is outside of the blocks:
{% with messages = get_flashed_messages(with_categories=true) %}
<p>1</p>
{% if messages %}
<p>2</p>
{% for message, category in messages %}
<p>3</p>
{% if category == 'success' %}
<p>4</p>
<div class='message success'>
<p>{{ message }}</p>
</div>
{% elif category == 'error' %}
<div class='message error'>
<p>{{ message }}</p>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
Python code(shortened to include relevant bits)
#app.route('/suggestion', methods=['GET', 'POST'])
def suggestion():
if request.method == 'POST':
...
content = f'Name: {name}\nEmail: {email}\nQuestion: {question}\nAnswer: {answer}\nType: {type}\nTopic: {topic}'
if sensible == 'Agreed' and accurate == 'Agreed':
email = Email()
sent = email.sendEmail(content)
if sent:
flash('Suggestion submitted successfully', category='success')
else:
flash('Error submitting suggestion. Please try again later',
category='error')
return redirect(url_for('suggest'))
Using the p tags to see where it's failing, I can tell that the {% if messages %} isn't working, but I don't know why.
I'm using flash('Message', category='success') to send the messages. I've looked at the Flask documentation and can't figure out what I'm doing wrong.
Please can someone help?
Have a look at this implementation
Routes.py
#auth.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
if request.method == "POST":
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.confirm_password(form.password.data):
login_user(user)
flash("Login successful", "success")
return redirect(url_for("home.index"))
flash("Invalid email or password", "error")
return render_template("auth/login.html", form=form)
_message.html template
<div class="flash-message">
<div class="msg-wrapper">
{% with messages=get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category,message in messages %}
<p class="{{category}} message">{{message}}</p>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
Looping through the messages and categories in reverse

Why is my wtforms error handling not working? [duplicate]

This question already has answers here:
Flask view return error "View function did not return a response"
(3 answers)
Closed 1 year ago.
I am trying to build a flask login and registration system. Everything works except when the user gives an incorrect input to the forms then there will be an error since there is no working error handling yet. I am using wtforms and here is the registration form class:
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email', validators=[DataRequired(),Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=4)])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), Length(min=4), EqualTo('password')])
submit = SubmitField('Sign Up')
And this is the registration route
#app.route('/register', methods=["GET", "POST"])
def register():
form = RegistrationForm()
if request.method == "POST":
if form.validate_on_submit():
return redirect(url_for('home'))
else:
return render_template('register.html', form=form)
I have tired to handle the errors in htmlbut every time the user gives the incorrect input then it show this error message "TypeError: The view function for 'register' did not return a valid response. The function either returned None or ended without a return statement." Here is my html form:
<form action="/register" method="POST">
<h1>Create Account</h1>
{{ form.hidden_tag() }} {% if form.username.errors %} {{
form.username(placeholder="Username", class="is-invalid") }} {% for
error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %} {% else %} {{ form.username(placeholder="Username") }} {%
endif %} {% if form.email.errors %} {{ form.email(placeholder="Email",
class="is-invalid") }} {% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %} {% else %} {{ form.email(placeholder="Email") }} {% endif
%} {% if form.password.errors %} {{
form.password(placeholder="Password", class="is-invalid") }} {% for
error in form.password.errors %}
<span>{{ error }}</span>
{% endfor %} {% else %} {{ form.password(placeholder="Password") }} {%
endif %} {% if form.confirm_password.errors %} {{
form.confirm_password(placeholder="Confirm Password",
class="is-invalid") }} {% for error in form.confirm_password.errors %}
<span>{{ error }}</span>
{% endfor %} {% else %} {{ form.confirm_password(placeholder="Confirm
Password") }} {% endif %}
<div>{{ form.submit(class="signup") }}</div>
</form>
Any response or help will be greatly appreciated. Thanks!
Every view must return something. Yours returns nothing if method is post but doesn't validate. It is a logical problem, not related to wtforms.
if request.method == "POST":
if form.validate_on_submit():
return redirect(url_for('home'))
else:
return 'oops'
else:
return render_template('register.html', form=form)

How do i create a progress bar with flask?

I'm developing a web interface by using flask.
The user log and reach an html page where a file can be upload. This file will be save and parse by an internal program (no relation with my problem)
I use a File Field in a Flask Form:
class MyForm(FlaskForm):
file = FileField("DROP YOUR FILE HERE", validators=[DataRequired()])
submit = SubmitField('Submit')
I call it in my main programm:
#app.route('/upload', methods=['GET', 'POST'])
#login_required
def uploadfile():
form = MyForm()
if form.validate_on_submit():
file = form.file
file.data.save(os.path.join("path/to/", secure_filename("filename")))
flash("File added")
return redirect(url_for('uploadfile'))
return render_template("upload.html",form=form)
and here is the body of my html page:
<form action="{{url_for('uploadfile')}}" method="post" enctype="multipart/form-data" novalidate>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p class="error">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
{{ form.csrf_token }}
{{form.fichier}}
{% for error in form.fichier.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<p>{{ form.submit() }}</p>
</form>
The problem is uploading and saving a lot of data take lots of time.
So i want to add a progress bar but i don't know how to get the download progress and send it to my html progress bar.
This is why i ask your help. If you have any solution i will take it.
Thanks you

Django Validation Error raised but not displayed for shopping cart app

I am running Django 2.2 and have written a simple shopping cart. I wish to validate two fields at the same time in such a way that both cannot be empty at the same time. In my forms.py,
from django import forms
class CartAddProductForm(forms.Form):
cc_handle = forms.CharField(required=False, label='CC Handle', empty_value='')
lc_handle = forms.CharField(required=False, label='LC Handle', empty_value='')
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('cc_handle') == '' and cleaned_data.get('lc_handle') == '':
print("issue detected")
raise forms.ValidationError('Either cc or lc handle is required.')
return cleaned_data
This is following the official Django docs on cleaning and validating fields that depend on each other. The print() statement above lets me know that the issue has been detected, i.e. both fields are empty. Running the Django server, I see that the issue was indeed detected but no validation error message was displayed on top of the originating page. The originating page is the product page that contains the product and a link to add the product to the shopping cart. Normally the validation error message is displayed at the top of the page.
According to the docs, the validation is done when is_valid() is called. So I put a diagnostic print of my views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from shop.models import Product
from .cart import Cart
from .forms import CartAddProductForm
#require_POST
def cart_add(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartAddProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product,
cc_handle=cd['cc_handle'],
lc_handle=cd['lc_handle'])
else:
print('invalid form')
return redirect('cart:cart_detail')
And indeed the words 'invalid form' popped up. The code then takes me to the shopping cart. Instead, what I want is to be at the product page and show the validation error informing the reader that both fields cannot be empty. Is there a simple way of doing it?
For required=True fields in the forms, if I leave it blank, there will be a message popping up saying that I need to fill it in. So I want to do something similar except the validation requires that both fields cannot be empty.
This is different from this Stackoverflow answer because that is a registration form. You can redirect it to the same form whereas for this case, the CartAddProductForm is embedded in all the products page on the site. If possible, I want the validation to occur at the same stage as the field with required=True option.
The product/detail.html template looks like the following.
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{{ product.name }}
{% endblock %}
{% block content %}
<div class="product-detail">
<img src="{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
<h1>{{ product.name }}</h1>
<h2>{{ product.category }}</h2>
<p class="price">${{ product.price }}</p>
<form action="{% url "cart:cart_add" product.id %}" method="post">
{{ cart_product_form }}
{% csrf_token %}
<input type="submit" value="Add to cart">
</form>
{{ product.description|linebreaks }}
</div>
{% endblock %}
Adding this line in form template has cleared your issue.
{{ cart_product_form.non_field_errors }}
product/detail.html:
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{{ product.name }}
{% endblock %}
{% block content %}
<div class="product-detail">
<img src="{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
<h1>{{ product.name }}</h1>
<h2>{{ product.category }}</h2>
<p class="price">${{ product.price }}</p>
<form action="{% url "cart:cart_add" product.id %}" method="post">
{{ cart_product_form }}
{% csrf_token %}
{{ cart_product_form.non_field_errors }} // This line will raise validation errors
<input type="submit" value="Add to cart">
</form>
{{ product.description|linebreaks }}
</div>
{% endblock %}
Doc:(Copied from official documentation)
Note that any errors raised by your Form.clean() override will not be
associated with any field in particular. They go into a special
“field” (called all), which you can access via the
non_field_errors() method if you need to. If you want to attach errors
to a specific field in the form, you need to call add_error().
Your view is inconditionnally redirecting to cart_details so no surprise you don't see the validation errors - you'd have to render the invalid form for this. You should only redirect when the post succeeded.

Prevent input type url default behavior using Flask

I have input type url in a form and Im trying to get the list of validation errors in the same page, but I want the form first send the data to the server and then render the error list but when the input type is set to url and I enter some other text It shows a popup and say Enter valid Url
It does it on the client side before sending any data to the server
I can use it with js and preventDefault but how can I overcome this default behavior using Flask
Here is my code
from flask_wtf import Form
from wtforms.fields import StringField
from flask.ext.wtf.html5 import URLField
from wtforms.validators import DataRequired, url
class BookmarkForm(Form):
url = URLField('url', validators = [DataRequired(), url()])
description = StringField('description')
the main py file
#app.route('/add', methods = ['GET', 'POST'])
def add():
form = BookmarkForm()
if form.validate_on_submit():
url = form.url.data
description = form.description.data
store_bookmark(url, description)
flash('stored "{}"'.format(description))
return redirect(url_for('index'))
return render_template('add.html', form = form)
and the template
<form id='addUrl' action='' method='post'
{% if form.url.errors %} class ='error'
{% endif %}>
{{ form.hidden_tag() }}
<p>Please enter your bookmark here</p>
{{ form.url(size = 50) }}
<p>Please enter additional description</p>
{{ form.description(size = 50) }}
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type='submit'>Submit</button>
</form>
It depends on the browser support of HTML5 and It causes client-side validation and prevents data to passed to the server. You can overcome that default behavior of the HTML5 URL field by adding the tag novalidate to your form.
Your template will look like this
<form id='addUrl' action='' method='post' novalidate
{% if form.url.errors %} class ='error'
{% endif %}>
{{ form.hidden_tag() }}
<p>Please enter your bookmark here</p>
{{ form.url(size = 50) }}
<p>Please enter additional description</p>
{{ form.description(size = 50) }}
{# <input type='text' name='url' required> #}
<ul>
{% for error in form.url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type='submit'>Submit</button>
</form>

Categories

Resources