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 %}
Related
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")
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 the Django for loop It am generating a paginated list from the database. This works. The generated list has a delete button to the right of each entry.
At the same page load I an using the the django for loop to generate individual Modal windows that match each delete button. So the button will call modal ID=### and there is a modal ### that matches. The HTML appears to render perfectly.
When I delete the TOP(First entry on the page) it works like a charm, I can do that all day long, with entries moving up and being deleted.
THE PROBLEM: When I choose a 2nd position or lower button the screen goes grey and freezes it needs a reload to respond again. This pattern is repeatable.
HTML:
{% load static %}
{% block content %}
<link rel="stylesheet" href="{% static "css/cabinet_table.css"%}">
<div class="col-lg-2">
</div>
<div class="col-lg-8">
<div class="table-responsive">
<table class="table table-hover">
<th colspan="3"style="text-align: center;"><h2>{{ user.username|capfirst }} Notes</h2></th>
{% for note in note_list %}
<tr>
<td >{{ note.title }}</td>
<td>{{ note.text|truncatewords:15 }}</td>
<td><button type="button" class="btn btn-info btn-success" data-toggle="modal" data-target="#deleteModal{{ note.id }}">Delete</button></td>
</tr>
{% endfor %}
</table>
</div>
<!-- Pagination below -->
{% if note_list.has_other_pages %}
<ul class="pagination">
{% if note_list.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in note_list.paginator.page_range %}
{% if note_list.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if note_list.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
<div class="col-lg-2">
</div>
{% include 'cabinet/_note_list_modal.html' %}
{% endblock %}
Included HTML(The modal generation):
{% for note in note_list %}
<!-- Modal {{ note.id }} -->
<div class="modal fade" id="deleteModal{{ note.id }}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel{{ note.id }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel{{ note.id }}">Delete Note</h4>
</div>
<div class="modal-body">
<h4>Title :</h4> {{ note.title }}<br>
<h4>Idea:</h4> {{ note.text|truncatewords:25 }}
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" onclick="settleUp{{ note.id }}()" >Delete</button>
</div>
</div>
</div>
</div>
<div id="hiddenForm{{ note.id }}" style="display: none" class="visibility=hidden">
<form class="" action="/fc/delete/{{ note.id }}" name="hiddenForm{{ note.id }}" method="post">
{% csrf_token %}
<input type="hidden" name="deleteNote{{ note.id }}" id="deleteNote{{ note.id }}" value="{{ note.id }}">
<!-- <button type="submit" name="betBalance">Update Balance</button> -->
</form>
<script type="text/javascript">
function settleUp{{ note.id }}(){
document.forms.hiddenForm{{ note.id }}.submit()
}
</script>
{% endfor %}
OBJECTIVE: Click on any delete button have its modal pop up and work.
Thanks for any help.
PS using inspect, which I don't know how to use well, I see no errors in the console.
The problem with this, which was solved in another question, is there is a missing DIV tag. Thanks for everyone who looked.
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>
In my flask web app, there is a route that display either all the member with their first name starting with 'A, B, C, etc..' or the Show All button that shows all the members registered . Right now it sum up to about 750 users. The problem is that displaying the 'show all' list takes over 10 seconds. The request to the DB itself is fast, it's the rendering that takes all those seconds. I was wondering if there is a way to speed this up? I'm pretty new to python, flask and jinja2 so I don't know the optimiztion paths yet.
Here is the views.py, when I click the 'Show all" button on the webpage, it calls the url with the letter argument set to '0':
#main.route('/all-members')
#login_required
#admin_required
def all_members():
alphabet = list(string.ascii_lowercase)
first_letter = request.args.get('letter')
if first_letter == '0':
user_ = User.query.order_by(User.fullname.asc()).all()
else:
user_ = User.query.filter(User.username.startswith(first_letter)).order_by(User.fullname.asc()).all()
return render_template('all_members.html', all_members = user_, alphabet = alphabet)
It return 750 user objects. I then pass that result to the template:
return render_template('all_members.html', all_members = user_, alphabet = alphabet)
And the template:
{% extends "base.html" %}
{% block title %}Zata - members list{% endblock %}
{% block page_content %}
<div class="page-header" xmlns="http://www.w3.org/1999/html">
<h1>Tribe members list</h1>
<p></p>
{% for letter in alphabet %}
<a class="btn btn-success" href="{{ url_for('.all_members', letter = letter) }}">{{ letter }}</a>
{% endfor %}
<a class="btn btn-info" href="{{ url_for('.all_members', letter = 0) }}">Show All</a>
</div>
{% for member in all_members %}
<div class="row">
<p>
<div class="col-md-2">
{{ member.fullname }}
</div>
<div class="col-md-1">
<a class="btn btn-warning btn-xs" href="{{ url_for('.edit_profile_admin', id=member.id) }}"> Edit Profile</a>
</div>
<div class="col-md-3">
<a class="btn btn-info btn-xs" href="{{ url_for('.add_punchcard', id=member.id, next=request.url) }}"> Manage Punchcard</a>
{% if member.punchcard_gold %}<span class="btn btn-xs" style="background-color:gold">G:{{ member.punchcard_gold }}</span>{% endif %}
{% if member.punchcard_normal %}<span class="btn btn-xs" style="background-color:#c0c0c0">R:{{ member.punchcard_normal }}</span>{% endif %}
</div>
<div class="col-md-4">
<a class="btn btn-info btn-xs" href="{{ url_for('.add_seasonpass', id=member.id, next=request.url) }}"> Manage Season pass</a>
{% if member.season_pass_gold %}<span class="btn btn-xs" style="background-color:gold">G:{{ member.season_pass_gold }}</span>{% endif %}
{% if member.season_pass_normal %}<span class="btn btn-xs" style="background-color:#c0c0c0">R:{{ member.season_pass_normal }}</span>{% endif %}
</div>
<div class="col-md-2">
<span class="btn btn-xs" style="background-color:yellowgreen">Class attended: {{ member.class_attended|length }}
</div>
</p>
</div>
{% endfor %}
{% endblock %}
The slow part is obviously the {% for member in all_members %} but is there another way to go through the list of user and build the page?
The problem isn't with your Jinja template but your Python code. You're returning all the records at the same time which slows down the execution. You need to perform some sort of pagination/lazy loading. If you're using Flask-SQLAlchemy, then it has a paginate method which will do all the work for you. For example, your query will look something like this
user_ = User.query.order_by(User.fullname.asc()).paginate(1, 10, True)
You can find more information here