Is there a way to use Bootstrap toast in Flask flashing natively? - flask

I want to show toast messages using flash() method in Flask. I know that Bootstrap alerts work, so I used the same logic and replaced the code for alerts with toasts. But it does not seem to work for me.
app.py
#app.route('/', methods=['GET', 'POST'])
def home():
if request.method == 'GET':
flash('This is a demo message.')
return render_template('home.html')
flash.html
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Message</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ message }}
</div>
</div>
</div>
{% endfor %}
{% endif %}
{% endwith %}
base.html
<html>
<head>
...
</head>
<body>
{% include 'flash.html' %}
....
</body>
</html>
home.html
{% extends 'base.html' %}
{% block main %}
...
{% endblock %}
I would like to know how can I achieve this natively in Flask. If not then I will need to explore Flask-Toastr (you can suggest alternatives in case)

This is absolutely achievable using the flash functionality in flask, and I wouldn't recommending using something like Flask-Toastr.
The flash functionality in flask can wrap any HTML.
The issue with your code is in your bootstrap/html, which you can test by removing all of the flash stuff and just having:
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Message</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ message }}
</div>
</div>
</div>
You'll see that the flash doesn't show there either. I suspect the issue is that you aren't initialising the toast using javascript as required. The docs for Bootstrap Toasts are here: https://getbootstrap.com/docs/5.0/components/toasts/
I recommend getting it working in plain HTML and then wrapping it in the flash.

Related

Flask.flash messages not available through extended template

I am having trouble with sending flashed messages to a route that extends its layout from another template. This message shows up just fine if use the message in the layout.html which makes me believe that rendering login.html first will render layout.html and use the flashed message there and not pass it to my /login route. How are you able to call this message in an extended template? I am using the jijna with syntax taken from here to be able to have the message variable available within my mainblock. Flask's documentation does not specify this either.
app.py
#app.route("/login", methods=["POST", "GET"])
def login():
# Forget any previous user
if session.get("user_id"):
session.pop("user_id")
if request.method == "POST":
# Create connection cursor
cursor = mysql.connection.cursor()
# Query database for email
cursor.execute("SELECT id, email, password FROM users WHERE email = %s", [request.form.get("email")])
row = cursor.fetchone()
print(row)
if row is None:
print("WHY")
flash("Invaid user")
return redirect("login")
My layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hoook - {% block title %}{% endblock %}</title>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, width=device-width">
<link href="/static/favicon-16x16.png" rel="icon">
<link href="/static/style.css" rel="stylesheet">
<!-- Scripts -->
<script src="https://kit.fontawesome.com/542c2d099e.js" crossorigin="anonymous"></script>
<script src="/static/mainJS.js"></script>
</head>
<body>
<div class="page-wrapper">
<header>
<nav class="main-navbar">
{% if request.path == "/login" %}
<div class="navbar-container login-container">
{% else %}
<div class="navbar-container">
{% endif %}
<div>
{% if request.path == "/login" %}
<img src="/static/hoook_logo_blue.png" alt="Hoook Logo" height="50" width="150">
{% else %}
<img src="/static/hoook_logo.png" alt="Hoook Logo" height="50" width="150">
{% endif %}
</div>
{% if request.path != "/login" %}
<div>
{% if session["user_id"] %}
{# change nav bar for logged in users #}
{% else %}
{# work on this nav bar for unlogged in users #}
{% if request.path == "/signup" %}
<a class="navbar-link" href="/login">Sign in</a>
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
</nav>
</header>
</div>
<main>
{% if request.path == "/login" %}
<div class="top-container signup-container">
{% else %}
<div class="top-container">
{% endif %}
{% with messages = get_flashed_messages() %}
{% block main %}{% endblock %}
{% endwith %}
</div>
</main>
<footer>
</footer>
</body>
</html>
My login.html
{% extends "layout.html" %}
{% block title %}
Login
{% endblock %}
{% block main %}
<div class="login-div">
<div>
<h1 class="color-control">Sign in to Hoook</h1>
</div>
<div class="login-input-bx">
<form action="/login" method="post" autocomplete="off">
<div class="form-control login-form-control">
<label class="login-label color-control" for="email">Email address</label>
<input class="login-input" type="text" name="email" id="email" required autofocus>
</div>
<div class="form-control login-form-control">
<label class="login-label color-control" for="password">Password</label>
<input class="login-input" type="password" name="password" id="password" required readonly onfocus="this.removeAttribute('readonly')">
</div>
<button class="btn btn-login" type="submit">Sign in</button>
</form>
</div>
{% if messages %}
{% for msg in messages %}
<div class="flashed-messages-div">
<p class="signup-para" id="login-flashed-messages">Error: {{ msg }}</p>
</div>
{% endfor %}
{% endif %}
<div class="signup-link-div">
<p class="color-control signup-login-font">New to Hoook? <a class="signup-link-anchor" href="/signup">Create an account</a>.</p>
</div>
</div>
{% endblock %}
Update
I guess I could do something like make_response instead as seen here. and just use:
response = make_response(render_template("login.html", message = "Invalid user"), 302)
return response
However I am curious if there is a way to pass the flashed message through instead.
I have had the same issue. Instead of:
return redirect("login")
try with:
return render_template("login.html")
The flashed message works that way for me.

Django messages + bootstrap toast. How to make it work?

Trying to get bootstrap popup and django messages to work. The problem is that I do not understand how to do it correctly so that if there is a message in the context, it would be displayed in the upper right part of the site.
Documentation: https://getbootstrap.com/docs/4.3/components/toasts/
Django v3.1.6 and Bootstrap v4.5
In the static files of the project there is bootstrap.bundle.js, it is also included in the base template. I'm not good at django in layout, so I will be very grateful for the most detailed answer.
You can modify the message classes shown in your template using MESSAGE_TAGS setting.
Add this to your settings.py
MESSAGE_TAGS = {
messages.DEBUG: 'alert-info',
messages.INFO: 'alert-info',
messages.SUCCESS: 'alert-success',
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-danger',
}
Then display them in your template (preferably at your base template)
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible shadow fade show" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{ message | safe }}
</div>
{% endfor %}
You need to
a) create a message in the view
b) add messages section to the template
in the template you need to add something like this:
{% if messages %}
<div class="add-your-bootstrap-classes-here-if-needed">
{% for msg in messages %}
<!-- add your message displaying html here -->
{% endfor %}
</div>
{% endif %}
in the view you just have to use one of the methods listed in Django messaging framework documentation https://docs.djangoproject.com/en/3.1/ref/contrib/messages/.
I use something like:
{% for message in messages %}
<div class="alert {% for tag in message.extra_tags.split %}
{{tag}} {%endfor%} fade show"
role="alert">
<div class="d-flex justify-content-end">
<button type="button"
class="close btn-sm btn btn-danger"
data-dismiss="alert"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<hr>
<p class="text-center">
{{ message | safe }}
</p>
</div>
{% endfor %}
{% block main%}
and in the view function, you should pass the extra_tags like this:
messages.success(self.request, "messge here", "alert-success alert-dismissible")

Django Calculation inside HTML

I wanted to do something simmilar to this suing django. but somehow i it doesn't work. how do i fix it?
for statik in statistik{
print(statik*total/100)
}
Is there any documentation regarding what I'm trying to implement to my django app? Thank you
Here's the HTML :
{% if statistics %}
{% for statik in statistics|slice:":4" %}
<div class="mb-3">
<div class="small text-gray-500">{{ statik.name }}
<div class="small float-right"><b>{{ statik.voters }} of {{ total }} Voters</b></div>
</div>
<div class="progress" style="height: 12px;">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ statik.voters * total/100 }}%" aria-valuenow="50"
aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
{% endfor %}
{% else %} <p>END TABLE</p>
{% endif %}
It is recommended you do calculations of any sort in view and pass it in context.
If you still want to go this route there are no math template tags except add included so you should create custom template tags or use django-mathfilters

Django, Bootstrap toast appears whenever I refresh the page instead of when needed

I want to make a toast appear only when a file has been processed.
Currently I check from the model.py whether another file exists or not.
If it does, the file has been processed. Else not if not.
def status(self):
if os.path.exists(...)):
return "processed"
else:
return "unprocessed"
I'm not sure if it's right to check from model.py.
As for my HTML file, I have set the following.
{% for td in user.userlist.all %}
{% if td.status == 'processed' %}
<div class="toast" drole="alert" data-autohide="false" ... >
<div class="toast-header">
<strong class="mr-auto">{{ td.status }}</strong>
<small class="text-muted">just now</small>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="toast-body">
The file was {{ td.status }}
</div>
</div>
{% endif %}
{% endfor %}
This works fine whenever it detects the word 'process', but the toast appears everytime I refresh since it detects 'process' repeatedly.
I want the toast appear only once when the file has actually processed instead of looking for the word 'process'.
Any suggestions are highly appreciated.
[SOLVED]
Added something like this on views.py
def process(request, obj_id):
if os.path.exists('...'):
messages.success(request, 'processed')
And on the HTML file, had to take it out of the for loop.
{% if messages %}
<ul class="messages">
{% for message in messages %}
<div class="toast" drole="alert" data-autohide="false" ... >
<div class="toast-header">
<strong class="mr-auto">Processed</strong>
<small class="text-muted">just now</small>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="toast-body">
{{ message }}
</div>
</div>
{% endfor %}
</ul>
{% endif %}
You can use the built-in messages framework to display toasts, that will give you better control of when and what to display.
You just need to add the message in your view:
messages.success(request, 'File processed')
And then display the messages in your template. You can even have that messages showing component included in your base template, and use the same component through all your different views. I use something like this (you'll need to adapt it to your Bootstrap specifics):
<div class="toast-messages" id="main-toasts">
{% for message in messages %}
<div class="toast {{ message.tags }}" role="alert">
{% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}<strong>Ouch!</strong> {% endif %}
{{ message }}
</div>
{% endfor %}
{% block toast_messages %}{% endblock toast_messages %}
</div>

Using Instagram API to make users populate images into my Django application directly from Instagram Pictures

My use case or what I want to do is:
Users click a Upload button,
it authenticates user,
its shows Instagram images,
users select, and
user click upload button to upload images or videos (media) from
Instagram directly into my django app.
What I have done is:
Instagram integration:
pip install python-instagram
views.py:
#login_required()
def instagram_pictures(request, template='dashboard/add-instagram-pictures.html'):
user = request.user
gallery = []
gallery = Gallery.objects.filter(service_provider=user.service_provider_profile)
context = {'gallery': gallery}
return render(request, template, context)
#login_required()
def add_instagram_pictures(request, template='dashboard/add-instagram-pictures.html'):
access_token = "YOUR_ACCESS_TOKEN" # Dont know how this gets factored in for any user
client_secret = settings.SECRET_ID
api = InstagramAPI(access_token=access_token, client_secret=client_secret)
recent_media, next_ = api.user_recent_media(user_id="userid", count=10)
for media in recent_media:
print(media.caption.text)
template/my-gallery.html:
{% extends 'base.html' %}
{% load static %}
{% block title %}My Dashboard{% endblock %}
{% block content %}
<div class="page-header" style="background: url({% static 'img/banner1.jpg' %});">
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="page-title">My Gallery</h1>
</div>
</div>
</div>
</div>
<div id="content">
<div class="container">
<div class="row">
{% include '_side_menu.html' %}
<div class="col-md-8 page-content">
<div class="inner-box text-center">
<a href="{% url 'dashboard:add-instagram-pictures' %}" class="btn btn-md btn-success">
<span class="fa fa-plus-circle"> Upload from Instagram</span>
</a>
<ul>
<!-- I will re-write this loop to redefine how images are rendered for the project. Maybe in Carousels-->
{% for p in photos %}
<li>{{ p.name }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
template/add-instagram-pictures.html:
{% extends 'base.html' %}
{% load static %}
{% block title %}Add Gallery{% endblock %}
{% block content %}
<div class="page-header" style="background: url({% static 'img/banner1.jpg' %});">
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="page-title">Add Instagram Pictures</h1>
</div>
</div>
</div>
</div>
<div id="content">
<div class="container">
<div class="row">
{% include '_side_menu.html' %}
<div class="col-md-8 page-content">
<div class="inner-box text-center">
<a href="{% url 'dashboard:my-gallery' %}" class="btn btn-md btn-success">
<span class="fa fa-chevron-circle-left"> Back to Album List</span>
</a>
</div>
</div>
</div>
</div>
</div> {% endblock %}Add
url.py:
from django.conf.urls import url, include
from dashboard.views import dashboard, my_services, add_service, my_gallery, add_gallery, bank_profile, add_bank_profile, add_instagram_pictures
def ajax_photo_upload_view(args):
pass
urlpatterns = [
url(r'^$', dashboard, {}, name='home'),
url(r'^bank-profile/$', bank_profile, {}, name='bank-profile'),
url(r'^add-bank-profile/$', add_bank_profile, name='add-bank-profile'),
url(r'^my-services/$', my_services, name='my-services'),
url(r'^add-service/$', add_service, name='add-service'),
url(r'^add-gallery/$', add_gallery, name='add-gallery'),
url(r'^my-gallery/$', my_gallery, name='my-gallery'),
url(r'^add-gallery/$', add_instagram_pictures, name='add-instagram-pictures'),
]
settings.py:
# instagram settings
CLIENT_ID = "XXXXXXXXXXXXXXXXXXXX"
SECRET_ID = "XXXXXXXXXXXXXXXXXXXX"
How do I go about my use case as I have stated, my solution is in no way closer to what I want to do.