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")
Related
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.
I included a contact-form on my webpage which looks like so:
I would like to style the "CC myself" - checkbox in the following ways:
The text "CC myself" is centered vertically within its box.
The checkbox should be right next to the text "CC myself".
The "forward"-symbol should be between text and checkbox, but directly next to the text and with more horizontal distance to the checkbox (on its right-hand side).
This is how I defined the contact form in forms.py:
from django import forms
class ContactForm(forms.Form):
# * Sender
from_email = forms.EmailField(
required=True,
label='Your Email',
widget=forms.TextInput(attrs={'placeholder': 'jsmith#example.com'}))
# * Optional CC to sender
cc_myself = forms.BooleanField(
required=False,
label='CC myself',
widget=forms.CheckboxInput(attrs={'class': 'fa fa-share'}))
# * Subject
subject = forms.CharField(required=True, label='Subject')
# * Message
message = forms.CharField(
widget=forms.Textarea(attrs={'placeholder': 'Dear Andreas ..'}),
required=True,
label='Message')
In the home.html template then, the form is displayed like so:
<form style="margin-left: auto;margin-right: 0;" method="post" action="{% url 'contact' %}">
{% csrf_token %}
<!-- * Neat autoformatting of the django-form via "pip install django-widget-tweaks"
Docs: https://simpleisbetterthancomplex.com/2015/12/04/package-of-the-week-django-widget-tweaks.html -->
{% for hidden in sendemail_form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in sendemail_form.visible_fields %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field|add_class:'form-control' }}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
</div>
{% endfor %}
<div style="text-align:center" class="form-actions">
<button type="submit" class="btn btn-success">
<!-- Search icons: https://fontawesome.com/icons?s=solid (syntax: class="fa fa-name-of-icon"))
Docs on how to implement: https://stackoverflow.com/questions/32612690/bootstrap-4-glyphicons-migration/41281304#41281304-->
<span class="fa fa-paper-plane"></span> Send Message
</button>
<!-- Add horizontal space: https://stackoverflow.com/questions/31198170/want-to-add-spacing-between-buttons/31199399#31199399 -->
<!-- Add cancel-button redirecting to "home" according to the following docs: https://simpleisbetterthancomplex.com/2015/12/04/package-of-the-week-django-widget-tweaks.html -->
<a href="{% url 'home' %}" class="btn btn-secondary">
<span class="fa fa-times"></span> Cancel
</a>
</div>
</form>
Edit on CSS-styling (bootstrap 4.x) employed in my project:
In my base.html wrapped around all my templates, the following is included:
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- Docs for including fontawesome: https://stackoverflow.com/a/41281304/12298276 -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
Edit after trying out the first suggestion of #Danoram:
The result looks like this (only modification is the place of the "forward" - symbol inside the checkbox:
I would like to display the help_text='Decide if the message should be forwarded to you.' below the checkbox in grey, just like with the other fields. Also, the checkbox is still much bigger regarding its height compared to the label text "CC myself". And last but not least, the formatting of the entire contact-form column has gotten distorted. Especially the latter should be fixed, otherwise I can't implement it this way.
This was my best attempt (conserving the size of the checkbox).
Using an if check to render the cc field separately.
Although to do this I had to remove the classes from the checkboxinput and put them directly into the html
forms.py
cc_myself = forms.BooleanField(
required=False,
label='CC myself',
widget=forms.CheckboxInput())
home.html
<form style="margin-left: auto;margin-right: 0;" method="post" action="{% url 'contact' %}">
{% csrf_token %}
<!-- * Neat autoformatting of the django-form via "pip install django-widget-tweaks"
Docs: https://simpleisbetterthancomplex.com/2015/12/04/package-of-the-week-django-widget-tweaks.html -->
{% for hidden in sendemail_form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in sendemail_form.visible_fields %}
{% if field.label == 'CC myself'%}
<div class="row form-group">
<div class="col-3 col-lg-2 col-xl-auto">
<label>{{ field.label }} <label for="{{ field.id_for_label }}" class="fa fa-share"></label></label>
</div>
<div class="col-2">
{{ field|add_class:'form-control' }}
</div>
</div>
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field|add_class:'form-control' }}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
</div>
{% endif %}
{% endfor %}
<div style="text-align:center" class="form-actions">
<button type="submit" class="btn btn-success">
<!-- Search icons: https://fontawesome.com/icons?s=solid (syntax: class="fa fa-name-of-icon"))
Docs on how to implement: https://stackoverflow.com/questions/32612690/bootstrap-4-glyphicons-migration/41281304#41281304-->
<span class="fa fa-paper-plane"></span> Send Message
</button>
<!-- Add horizontal space: https://stackoverflow.com/questions/31198170/want-to-add-spacing-between-buttons/31199399#31199399 -->
<!-- Add cancel-button redirecting to "home" according to the following docs: https://simpleisbetterthancomplex.com/2015/12/04/package-of-the-week-django-widget-tweaks.html -->
<a href="{% url 'home' %}" class="btn btn-secondary">
<span class="fa fa-times"></span> Cancel
</a>
</div>
</form>
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>
I have a view in which I send some messages, like this:
messages.warning(request, 'Nothing found")
In my settings.py I have edited the message tags like:
MESSAGE_TAGS = {
messages.DEBUG: 'alert-info',
messages.INFO: 'alert-info',
messages.SUCCESS: 'alert-success',
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-danger'
}
on my template, I display them like this:
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible fade in">
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{% if message.tags == 'info' %}
<p>{{ message }}<br>
You have <strong>{{users}}</strong> user{{users|pluralize}}<br>
,around <strong>{{non_users}}</strong> non user{{non_users|pluralize}}</p>
{% else %}
<p>{{ message }}</p>
{% endif %}
</div>
{% endfor %}
When the template loads, the messages appears very fast and disappears.
If I try this: <p>{{message}}</p> instead the message stays there, so I'm guessing I'm doing something wrong with bootstrap.
This (kinda) works as well: <div class="{{message.tags}}">
Could someone shed a light?
Edit:
I just noticed this is happening specifically when I use messages.error(), the rest of them are working properly, can't seem to find what's wrong.
try this, the class is alert-{{tag}} which will render alert-info, there's no whitespace
{% for message in messages %}
<div class="alert alert-{{message.tags}} page-alert">
<button type="button" class="close" data-dismiss='alert'>
<span aria-hidden="true"> × </span>
<span class="sr-only">Close</span>
</button>
<p>{{message}}</p>
</div>
{% endfor %}
I have a form in my django project and I want to display the form errors if any when the user clicks submit. This is one of my fields (working on getting one right before I move on to others)
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="title">Title
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
{{ form.title }}
</div>
<div class="col-md-3 .col-md-offset-3">
{# <p class="text-danger">{% for error in form.title.errors %}{{ error }}{% endfor %}</p>#}
<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>
<p>{% for error in form.title.errros %} {{ error }} {% endfor %}</p>
</div>
</div>
However I have 2 issues :
The alert shows up when the form loads, without the error message:
When I dismiss the alert the form styling gets messed up:
I cant seem to understand what the issue is.
I was able to get the error message without the bootstrap alerts using this code. However it would be great to let the user dismiss an alert after the correction has been made:
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="title">Title
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
{{ form.title }}
</div>
<span class="help-block">
<p class="text-danger">{% for error in form.title.errors %}{{ error }}{% endfor %}</p>
</span>
</div>
Your <div> containing the alert has no closing </div> tag, that messes up the form when you dismiss the alert. As for the alert showing up when the form loads - there's already some content in the alert div (the 'close' button) so it is displayed. You have to render it conditionally (only when there are corresponding errors to show):
<div class="col-md-3 .col-md-offset-3">
{% if form.title.errors %}
<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>
<p>{% for error in form.title.errors %} {{ error }} {% endfor %}</p>
</div>
{% endif %}
</div>