How to show a modal notification using flask framework and python? - python

I am working on building a web application using Flask framework and Python.
Using one of the html pages, i am getting inputs from the user and processing them once the user clicks on the Submit button. The requirement is that, once the user clicks on the Submit button, i would like to show a modal notification (or any notification) that the data is being processed.
The code for the Submit button in process_data.html is -
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
I tried adding modal code to it as follows -
<div class="form-group">
{{ form.submit(class="btn btn-outline-info", data-toggle="modal", data-target="#exampleModal") }}
</div>
but it failed with jinja2.exceptions.TemplateSyntaxError: invalid syntax for function call expression.
routes.py code -
#app.route("/process_data", methods=['GET','POST'])
def process_data():
form = ProcessDataForm()
if form.validate_on_submit():
posts = get_data('process_data', version=form.version.data, cn=form.cn.data, ip=form.ip.data)
if posts:
flash(f'Processing Complete!','success')
else:
flash(f'Processing failed','warning')
return render_template('process_data.html', title='Process Data', form=form, posts=posts)
return render_template('process_data.html', title='Process Data', form=form)
Can someone please help? Thanks!

You can do it using Message Flash.
A sample code is provided here:
from flask import flash
#app.route("/process_data", methods=['GET','POST'])
def process_data():
form = ProcessDataForm()
if form.validate_on_submit():
flash('Data is being processed')
posts = get_data('process_data', version=form.version.data, cn=form.cn.data, ip=form.ip.data)
return render_template('process_data.html', title='Process Data', form=form, posts=posts)
return render_template('process_data.html', title='Process Data', form=form)
In you html :
<!doctype html>
<title>My Application</title>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block body %}{% endblock %}
Please find the documentation for more explaination.

Related

Flask form not validating when csrf token is included

I have a flask form that I am using to get input to pass into a class. When I am trying to submit it, it is not showing any error and just reloading the same page. I have made sure that the csrf token is included in the jinja template as well. Here is the code:
Forms.py
class addTripForm(FlaskForm):
location = StringField('Location', validators=[DataRequired()])
startdate = DateField('Start Date', format='%Y=%m-%d', validators=[DataRequired()])
enddate = DateField('End Date', format='%Y=%m-%d', validators=[DataRequired()])
submit = SubmitField('Submit')
app.py
def check_form(form):
print(form.errors)
if form.submit():
print("submitted")
if form.validate():
print("validated")
#app.route('/add', methods=['GET','POST'])
def add():
form = addTripForm(request.form)
check_form(form)
if form.validate_on_submit():
# if form is valid, use fields to create and store a trip
print("adding trip")
user.add_trip(form.location.data,
form.startdate.data,
form.enddate.data)
return redirect('/trips')
return render_template('add.html', form=form)
add.html
{% extends 'base.html' %}
{% block content %}
<body>
<h3> Select a location and date range</h3>
<form action="" method="POST" name="addtrip">
{{ form.csrf_token }}
{{ form.location.label }} {{form.location}}
{{ form.startdate.label }} {{ form.startdate(class='datepicker') }}
{{ form.enddate.label }} {{form.enddate(class='datapicker')}}
<p>
{{ form.submit(class='btn btn-primary') }}
</p>
</form>
</body>
{% endblock %}
You should change your DateFields' format to '%Y-%m-%d' from '%Y=%m-%d' and then your code will work. The date value is formatted as yyyy-mm-dd in the HTTP Request.

I'm getting a NoReverseMatch error but I think my files are valid

From my base.html:
<div id="button_container">
<button class="button"> HOME </button>
<button class="button"><a href="{% url 'rooms' %}"> ROOMS & SUITES </button>
<button class="button"><a href="{% url 'roomBookingsList' %}"> ROOM BOOKINGS </button>
</div>
Urls.py:
path('rooms/mybooking', views.roomBookingsList, name='roomBookingsList'),
Views.py:
def roomBookingsList(request):
suiteBookingList = Suite_booking.objects.all()
context = {
'suiteBookingList': suiteBookingList,
}
return render (request, 'roomBookingsList.html', context=context)
roomBookingsList.html:
{% for suiteBookingList in suiteBookingList %}
<li> {{ suiteBookingList }}</li>
{% endfor %}
So when I run it and click the ROOM BOOKINGS from the base.html, it will show me whatever rooms I have booked. No problem here. I also want to add an edit button here so I can update the booking if I accidentally typo the name or something.
So I added in the roomBookingsList.html:
{% for suiteBookingList in suiteBookingList %}
<li> {{ suiteBookingList }}</li> - Edit
{% endfor %}
Added in urls.py:
path('rooms/suite/edit/<int:suite_id>', views.edit_suite, name='edit_suite'),
Added in views.py:
def edit_suite(request, suite_id):
if request.method == 'POST':
suite = Suite_booking.objects.get(pk=suite_id)
form = BookingForm(request.POST, instance=suite)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('roomBookingsList'))
else:
suite = Suite_booking.objects.get(pk=suite_id)
fields = model_to_dict(suite)
form = BookingForm(initial=fields, instance=suite)
context = {
'form': form,
'type': 'edit',
}
return render(request, 'roomBookingsList.html', context=context)
Then I tried to run it again, and this time when I click the ROOM BOOKINGS button, it gave an error:
NoReverseMatch at /system/rooms/mybooking
Reverse for 'edit_suite' with arguments '('',)' not found. 1 pattern(s) tried: ['system/rooms/suite/edit/(?P<suite_id>[0-9]+)$']
In the traceback there's this:
D:\ssip\hotel\system\views.py, line 59, in roomBookingsList
59. return render (request, 'roomBookingsList.html', context=context)
I tried searching all the files I used for the ('',) but didn't find any. I also looked over my def roomBookingsList but didn't find anything odd in it. I already tried searching the error but most answers were OP forgetting the dot or underscore when passing args.
Is it because I'm calling Suite_booking object in both the def roomBookingsList and edit_suite? What should I change here?
Sorry it's long and thank you in advance.
It seems like suite.id does not exist. Shouldn't it be suiteBookingList.id instead?
{% for suiteBookingList in suiteBookingList %}
<li> {{ suiteBookingList }}</li> - Edit
{% endfor %}
And though it works, I would recommend not using the same name as your variable to iterate:
{% for suite in suiteBookingList %}
<li> {{ suite }}</li> - Edit
{% endfor %}

Flask WTForms Validate on Submit not working

I am working on a page in my application where a user submits a review on a review page using WTForms, and upon clicking the submit button, the text "Success" should display. This is currently not happening, as upon clicking submit, the review page simply reloads itself but with an error.
I have created other pages requiring form validation which has worked, except for this one and I can't seem to figure out why, despite replicating the code from other pages.
Any insights are much appreciated!
Screenshot of error image
Here is my HTML code
<html>
<body>
{% for books in books %}
{{books.title}}
{% endfor %}
<form action = "{{ url_for('review', isbn=books.isbn)}}", method = "POST">
<div>
<br>{{ form.review(class_='form-control',placeholder ='Leave a review here')}}</br>
<ul>
{% for error in form.review.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<br>{{ form.rating(class_='form-control',placeholder ='Leave a rating here')}}</br>
<ul>
{% for error in form.rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{{form.csrf_token}}
{{form.submit_button }}
</div>
</form>
</body>
</html>
Python Code
#app.route("/review/<string:isbn>", methods = ['GET', 'POST'])
#login_required
def review(isbn):
review = Books.query.filter_by(isbn=isbn).all()
review_form = ReviewForm()
if review_form.validate_on_submit():
return "Success"
return render_template("review.html", books = review, form = review_form)
WTForms fields
class ReviewForm(FlaskForm):
""" Review """
review = StringField('review_label', widget=TextArea(), validators = [InputRequired(message = "Review can't be empty")])
rating = StringField('rating_label', validators= [InputRequired(message="Please input a rating")])
submit_button = SubmitField('Submit')
I would suggest the following. Let me know if this helps! :)
from markupsafe import escape
#app.route("/review/<string:isbn>", methods = ['GET', 'POST'])
#login_required
def review(isbn):
review = Books.query.filter_by(isbn=isbn).all()
review_form = ReviewForm()
if review_form.validate_on_submit():
return "Success %s" % escape(isbn)
return render_template("review.html", books = review, form = review_form)
reference flask documentation

Use of with for getting flashed messages in flask [duplicate]

This question already has answers here:
How does the 'with' statement work in Flask (Jinja2)?
(2 answers)
Closed 4 years ago.
I was checking the message flashing in flask framework from here.
This is a basic example where a template file (Index.html) is used to provide the initial link and another template file (Login.html) creates the form.
The files are:
Login.html:
<!doctype html>
<html>
<body>
<h1>Login</h1>
{% if error %}
<p><strong>Error:</strong> {{ error }}
{% endif %}
<form action = "" method = post>
<dl>
<dt>Username:</dt>
<dd>
<input type = text name = username
value = "{{request.form.username }}">
</dd>
<dt>Password:</dt>
<dd><input type = password name = password></dd>
</dl>
<p><input type = submit value = Login></p>
</form>
</body>
</html>
Index.html:
<!doctype html>
<html>
<head>
<title>Flask Message flashing</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li<{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h1>Flask Message Flashing Example</h1>
<p>Do you want to <a href = "{{ url_for('login') }}">
<b>log in?</b></a></p>
</body>
</html>
Flash.py:
from flask import Flask, flash, redirect, render_template, request, url_for
app = Flask(__name__)
app.secret_key = 'random string'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/login', methods = ['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or \
request.form['password'] != 'admin':
error = 'Invalid username or password. Please try again!'
else:
flash('You were successfully logged in')
return redirect(url_for('index'))
return render_template('login.html', error = error)
if __name__ == "__main__":
app.run(debug = True)
The part that is confusing me is inside index.html. It's using with messages = get_flashed_messages() to get the messages from the session. I do not fully understand why it's using with? I know with is used for resources, files, streams etc to control the closing procedure (and for not leaving something open when something goes wrong etc). What's the resource it's accessing using with in this context?
I tried removing it (in this context) and an error occurred:
jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag
'messages'.
Also, example use cases from programcreek.com does not use with with get_flashed_messages so what's the case here?
Jinja templates are not Python. with in a template is not a Python context manager, it just introduces a new scope; this code definesa new variable messages that is only visible until the endwith.
See the docs.
try to fix index.html
<!doctype html>
<html>
<head>
<title>Flask Message flashing</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h1>Flask Message Flashing Example</h1>
<p>Do you want to <a href="{{ url_for('login') }}">
<b>log in?</b></a></p>
</body>
</html>

How to display flashing message without reloading the page in Flask?

I'm working on a web application using Flask in Python.
I have small function in my application that calculates some values in the background and displays the result on the web page via a flashing message.
Everything is displaying and working fine but it requires page reloading to get the flashing message.
I want to display messages without reloading page.
I heard that I can do that with js, but I'm not familiar with js.
If you have any ideas or suggestion I would appreciate.
There is my code that could build a better picture of what I'm doing.
This is the renderer between my app and the main html file
{% 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 %}
This is the html file were I want to display flashing messages:
<div class="container-fluid" style="min-height:100%">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{message}}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
Here's what Flask Web Development: Developing Web Applications with Python (pp. 46-48) has to say of Message Flashing:
Sometimes it is useful to give the user a status update after a request is completed. This
could be a confirmation message, a warning, or an error. A typical example is when you
submit a login form to a website with a mistake and the server responds by rendering
the login form again with a message above it that informs you that your username or
password is invalid.
Flask includes this functionality as a core feature. Example 4-6 shows how the flash()
function can be used for this purpose.
Example 4-6. hello.py: Flashed messages
#app.route('/', methods=['GET', 'POST'])
def index():
form = Nameform()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
form = form, name = session.get('name'))
In this example, each time a name is submitted it is compared against the name stored
in the user session, which would have been put there during a previous submission of
the same form. If the two names are different, the flash() function is invoked with a
message to be displayed on the next response sent back to the client.
Calling flash() is not enough to get messages displayed; the templates used by the
application need to render these messages. The best place to render flashed messages is
the base template, because that will enable these messages in all pages. Flask makes a
get_flashed_messages() function available to templates to retrieve the messages and
render them, as shown in Example 4-7.
Example 4-7. templates/base.html: Flash message rendering
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
In this example, messages are rendered using Bootstrap’s alert CSS styles for warning
messages (one is shown in Figure 4-4).
Figure 4-4. Flashed message
A loop is used because there could be multiple messages queued for display, one for
each time flash() was called in the previous request cycle. Messages that are retrieved from get_flashed_messages() will not be returned the next time this function is called,
so flashed messages appear only once and are then discarded.
This is not possible via Python without reloading the page. You must do this in javascript. I suggest CSS styling with display: none and display: block. Here is an example.
1) Python Code, this should go in your app.py or flask.py file.
app.route('/flash/<message>')
def flash(message):
return render_template('flash.html', msg=message)
This will render the HTML page named flash.html. The URL passed in will also have another argument, <message> this is the message that will flash. A URL like this, localhost:80/flash/Hello%20World! will flash the message "Hello World!" on your screen.
There is also another way to pass a message in, this is will arguments. The code for that is like so.
app.route('/flash')
def flash():
message = request.args.get("msg")
return render_template("flash.html", ,msg=message)
This uses the flask's request arguments. So a URL like this, localhost:80/flash?msg=Hello%20World! will give a flashing message saying "Hello World!". If you want to use this method be sure to have the import statement, from flask import request in your import statements.
2) Html Code, this is a separate file named, flash.html in your templates folder.
<body>
<h1 id="header">{{ message }}</h1>
<script>
var heading = $("#header");
setInterval(function() {
if (heading.style.display == "block") { heading.style.display = "none"; }
else if (heading.style.display == "none") { heading.style.display = "block"; }
}, 1000);
</script>
</body>
The 1000 in the setInterval is milliseconds. So the heading will blink every 2 seconds.
You may want to consider using Toastr instead. I ran into the same roadblock with Flask's Flash feature, and Toastr is pure JS. You can use it just like a console log line in your code
toastr.info("Here's a message to briefly show to your user");

Categories