Flask: Flash function ignoring the 'category' argument - python

So I write the controller:
#app.route('/')
def index():
flash('Hello world!', 'success')
return render_template('index.html')
then in my template I output the flash messages like this:
{%- with messages = get_flashed_messages(with_categories=true) -%}
{%- if messages -%}
<ul class="flashes unstyled">
{%- for category, message in messages -%}
<li class="alert alert-{{ category }}">
<a class="close" data-dismiss="alert">×</a>
{{ message }}
</li>
{%- endfor -%}
</ul>
{%- endif -%}
{%- endwith %}
But the issue is that I ALWAYS get just 'message' category so <li> goes with classes 'alert alert-message'.
I read the docs and as to me I did everything right, but 'flash' function ignoring the second argument and always uses the default value 'message' (instead of given by me 'success').
I wonder if anyone had that issue and know how to handle it?

Edit: Based on other comments and testing the kwarg is unnecessary.
Based on the docs at http://flask.pocoo.org/docs/api/#message-flashing it appears you need to use this format. flash(message, category='message')
#app.route('/')
def index():
flash('Hello world!', category='success')
return render_template('index.html')

When you call get_flashed_messages(with_categories=True), it return a list of tuples in the form (category, message).
So now you can use flash() like this:
flash('some message', 'success') # `category=` is needn't
In html, you can just loop the message in the call:
{% for message in get_flashed_messages(with_categories=True) %}
<div class="alert alert-{{ message[0] }}">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message[1] }}
</div>
{% endfor %}

Related

Error message not working as expected in Django

I am trying to display a message on order creation success or failure. For messages.success(request, "My message") it is working as expected. But for messages.error(request, "My message") it is not as expected. I read the django messages framework docs, but no use. Can someone tell me why is this happening
Success Message:
Failed Message:
This is supposed to be red alert if I am not wrong.
Here's my html file.
base.html
<main role="main" class="container" >
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
views.py
if verify:
if response_dict['RESPCODE'] == '01':
messages.success(
request, "Thank you for ordering! Your items will be delivered soon")
return redirect(reverse('update-records', kwargs={'order_id': order_id}))
else:
messages.error(
request, "Your order could not be placed, here are the details: " + response_dict['RESPMSG'])
return redirect(reverse('profile-page'))
If the problem is alert-error not working you can use this after have the import statement of your message :
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
Reference ->
https://docs.djangoproject.com/en/3.0/ref/contrib/messages/#message-tags
#Sowjanya R Bhat
The method you suggest can be hardcoded but I need to know, why isn't it implementing red alert my default. Your suggestion works however
<main role="main" class="container" >
{% if messages %}
{% for message in messages %}
{% if message.tags == "error"%}
<div class="alert alert-danger">
{{ message }}
</div>
{% else %}
<div class="alert alert-success">
{{ message }}
</div>
{% endif %}
{% endfor %}
{% endif %}
In your HTML:
<script>
setTimeout(function () {
$('#flash').fadeOut('fast');
},5000);
</script>
<div id="flash">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags}} m-0" role="alert">
<strong>{{ message }}</strong>
</div>
{% endfor %}
{% endif %}
</div>
in django settings.py:
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
in django views:
from django.contrib import messages
if verify:
if response_dict['RESPCODE'] == '01':
messages.success(
request, "Thank you for ordering! Your items will be delivered soon")
return redirect(reverse('update-records', kwargs={'order_id': order_id}))
else:
messages.error(
request, "Your order could not be placed, here are the details: " + response_dict['RESPMSG'])
return redirect(reverse('profile-page'))
just use messages.warning instead, it would at least show a color.
This is because alert-error is not a bootstrap class.
The according class is named alert-danger.
Generally the tags line up nicely between bootstrap and django, this is why your code works. But as you see "error" != "danger".
To fix the issue replace
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
with
{% if message.tags == "error" %}
<div class="alert alert-danger">
{{ message }}
</div>
{% else %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endif %}

How to flash success and danger with different messages in flask.

I have a flask application where I want to flash different alert messages depending on an action.
here is my first message, where i check if an email matches my regex pattern:
reciver = str(email)
if not re.match(r"[^#]+#[^#]+\.[^#]+", reciver):
flash("Please enter a valid email address", 'error')
return render_template("about.html")
here is the second message, which is displayed if a message is sent successfully:
flash('Message sent succesfully', 'succes')
return render_template("about.html")
here is my HTML code:
<h1 class="mb-5"> Enter your message, and i will get back to you as soon as possible</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-success">
<li>{{ message }} </li>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block body %}{% endblock %}
how can I make an alert-danger for the first message and an alert-success for the second message, is there some sort of conditional that can be used?
You can use the following pattern to specify the category parameter of the flash function.
:param category: the category for the message. The following values
are recommended: 'message' for any kind of message,
'error' for errors, 'info' for information
messages and 'warning' for warnings. However any
kind of string can be used as category.
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert {{ category }}"> {{ message|capitalize }} </div>
{% endfor %}
{% endif %}
{% endwith %}
By putting category in the class attribute, you can associate special colors with some css rules like :
.alert.success {
background-color: green;
}
.alert.error {
background-color: red;
}
flash('Message sent successfully', 'success')
flash("Please enter a valid email address", 'error')
These calls will generate:
<div class="alert success"> Message sent successfully </div>
<div class="alert error"> Please enter a valid email address </div>
Official documentation : http://flask.pocoo.org/docs/1.0/patterns/flashing/#flashing-with-categories
With flask 1.0.2 this similar but slightly different approach worked for me - slightly simpler I think:
flash('This is an error message in red', 'danger')
flash('This is an informational message in blue', 'info')
In Flask HTML template:
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}" role="alert"> {{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
This way I didn't need to define any CSS and just used what was already in the default Flask distribution.

How to display html content through flask messages?

I understand that flash() takes only string and displays that in the redirected page.
I m trying to send html through flash
message = "<h1>Voila! Platform is ready to used</h1>"
flash(message)
return render_template('output.html')
output.html
<div class="flashes">
{% for message in get_flashed_messages()%}
{{ message }}
{% endfor %}
</div>
But it is displaying as string <h1>Voila! Platform is ready to used</h1> is there any way to overcome this.
Where possible, a secure approach is to wrap your string in a Markup object before passing it to the template:
Python code:
from flask import Markup
message = Markup("<h1>Voila! Platform is ready to used</h1>")
flash(message)
return render_template('output.html')
Jinja2 Template:
<div class="flashes">
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
</div>
Using {{message|safe}} will work, but also opens up the door for an attacker to inject malicious HTML or Javascript into your page, also known an an XSS attack. More info here if you're interested.
Use the safe filter:
<div class="flashes">
{% for message in get_flashed_messages()%}
{{ message|safe }}
{% endfor %}
</div>
For cases where you might want to control the CSS applied depending on the status of message (Success | Error), the following code might be useful:
{% for category, msg in get_flashed_messages(with_categories=true) %}
<div class="alert {{ category }} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
{{ msg|safe }}
</div>
{% endfor %}
Another way is to call render_template to the external HTML file and passing that to Markup class.
main/routes.py
from flask import render_template, flash, Markup
from . import blueprint
#blueprint.route("/user")
def user():
flash(Markup(render_template("templates/user.html", name="John Doe"))
return render_template("templates/home.html")
templates/user.html
<h1>User: {{ name }}</h1>

unable to show custom error message in Jinja2 template

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 %}

WTForms getting the errors

Currently in WTForms to access errors you have to loop through field errors like so:
for error in form.username.errors:
print error
Since I'm building a rest application which uses no form views, I'm forced to check through all form fields in order to find where the error lies.
Is there a way I could do something like:
for fieldName, errorMessage in form.errors:
...do something
The actual form object has an errors attribute that contains the field names and their errors in a dictionary. So you could do:
for fieldName, errorMessages in form.errors.items():
for err in errorMessages:
# do something with your errorMessages for fieldName
A cleaner solution for Flask templates:
Python 3:
{% for field, errors in form.errors.items() %}
<div class="alert alert-error">
{{ form[field].label }}: {{ ', '.join(errors) }}
</div>
{% endfor %}
Python 2:
{% for field, errors in form.errors.iteritems() %}
<div class="alert alert-error">
{{ form[field].label }}: {{ ', '.join(errors) }}
</div>
{% endfor %}
For anyone looking to do this in Flask templates:
{% for field in form.errors %}
{% for error in form.errors[field] %}
<div class="alert alert-error">
<strong>Error!</strong> {{error}}
</div>
{% endfor %}
{% endfor %}
With ModelFormFields in SqlAlchemy when used with WTForms, if you have a nested object inside an object (foreign key relationships), here is how you show the relevant errors for fields properly.
Python side:
def flash_errors(form):
for field, errors in form.errors.items():
if type(form[field]) == ModelFormField:
for error, lines in errors.iteritems():
description = "\n".join(lines)
flash(u"Error in the %s field - %s" % (
#getattr(form, field).label.text + " " + error,
form[field][error].label.text,
description
))
else:
for error, lines in errors.iteritems():
description = "\n".join(lines)
flash(u"Error in the %s field - %s" % (
#getattr(form, field).label.text + " " + error,
form[field].label.text,
description
))
Jinja side:
{% with messages = get_flashed_messages(with_categories=true) %}
{% for message in messages %}
{% if "Error" not in message[1]: %}
<div class="alert alert-info">
<strong>Success! </strong> {{ message[1] }}
</div>
{% endif %}
{% if "Error" in message[1]: %}
<div class="alert alert-warning">
{{ message[1] }}
</div>
{% endif %}
{% endfor %}
{% endwith %}
Hope that helps.

Categories

Resources