Displaying a message as a popup/alert in Django? - python

I have a form that, upon submission it just redirects to itself again. I'm trying to display a message that says "You have entered (work_area)." Currently, I only got the displaying part without the variable in it, but it shows within the html after the user submits it. I would like it to be a popup or alert message that the user can close after they see it, but I don't understand how this would work in Django.
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
#success_message = "You have logged into %(work_area)s"
def form_valid(self, form):
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in self.request.POST:
form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(station_number=station)).update(time_in=datetime.now())
messages.info(self.request, "You have entered")
return HttpResponseRedirect(self.request.path_info)
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" class="form-horizontal" novalidate >
{% csrf_token %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<div>
<div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
{% endblock main %}
And it displays like this:
Edit:

This is more a javascript question but anyways, this is how I handle this (if you are using bootstrap):
1) Create a message.html file with the following content:
{% for message in messages %}
<div class="toast notification bg-{% if message.tags == 'error' %}danger{% else %}{{message.tags}}{% endif %}" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
<div class="toast-header">
<strong class="mr-auto">
{% if message.tags == 'error' %}
<i class="fas fa-times mr-2"></i>
{% elif message.tags == 'warning' %}
<i class="fas fa-exclamation mr-2"></i>
{% elif message.tags == 'info' %}
<i class="fas fa-info mr-2"></i>
{% elif message.tags == 'success' %}
<i class="fas fa-check mr-2"></i>
{% endif %}
{{message.tags|capfirst}}
</strong>
<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|safe}}
</div>
</div>
{% endfor %}
2) Change your base.html and add:
...
{% include 'message.html' %}
...
<!-- and at the end:-->
<script src="{% static 'js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% if messages %}
<script>
{% for message in messages %}
$(document).ready(function () {
$('.toast').toast('show');
});
{% endfor %}
</script>
{% endif %}
Also, don't forget to include bootstrap.
This way you can use django messages on all of your templates, without explicitly change them.
Edit
You also need this custom css class in order for your notification to show on top right of the page:
.notification{
position: absolute;
top: 5rem;
right: 1rem;
}

Django has built-in functions for that which is messages.
messages.success(self.request,"message sent")
In your HTML, make sure to create a message class.
{% if message.tags %} class="{{message.tags}}"{% endif %}

Related

Button must have a tooltip in one of template, but not in the others

I include the button for different templates (and different divs). There should be a tooltip in one of these divs, but not in the others. How can I do this?
I think I can check the div class in the button (something like {% if div_class=="..."%}), but I do not know how to do this. Is it possible? Maybe I need to check the template name or something else?
first.html
<div class="single-card__favorite">
{% include 'button.html' %}
</div>
second.html
<div class="card__footer">
{% include 'button.html' %}
</div>
button.html
{% load user_filters %}
{% if author|isfavorite:user %}>
<button class="button button_style_none" name="favorites">
<span class="icon-favorite icon-favorite_active"></span>
</button>
<div class="single-card__favorite-tooltip tooltip">Delete from favorites</div>
{% else %}
<button class="button button_style_none" name="favorites" data-out>
<span class="icon-favorite"></span>
</button>
<div class="single-card__favorite-tooltip tooltip">Add to favorites</div>
{% endif %}
You can pass additional context to the included template using the with clause (Reference: documentation). So you can pass some variable that will indicate that the tooltip should be rendered:
first.html:
<div class="single-card__favorite">
{% include 'button.html' with tooltip="yes" %}
</div>
button.html:
{% load user_filters %}
{% if author|isfavorite:user %}>
<button class="button button_style_none" name="favorites">
<span class="icon-favorite icon-favorite_active"></span>
</button>
{% if tooltip == "yes" %}
<div class="single-card__favorite-tooltip tooltip">Delete from favorites</div>
{% endif %}
{% else %}
<button class="button button_style_none" name="favorites" data-out>
<span class="icon-favorite"></span>
</button>
{% if tooltip == "yes" %}
<div class="single-card__favorite-tooltip tooltip">Add to favorites</div>
{% endif %}
{% endif %}
If your URLs are different for each template.
Suppose you have 2 URLs:
1. domain.com/url1_bro
2. domain.com/url2_bro
Then you can do something like this:
{% if 'url1' in request.path %}
<!--add tooltip html-->
{%else%}
<!--html button without tooltip-->
{%endif%}

Page does not redirect on click but other buttons work/redirect properly?

I have a page that contains a form. It has 3 buttons, Enter/Leave and Options. My enter and leave button operate just fine, but the options button is supposed to redirect to a list of entries and currently it does not do anything, not even produce errors, which I can't figure out why it's happening.
I feel like I'm missing something very slight, I tried moving the Manager Options button into the form tags but this did not work either, so I'm not sure I'm missing an important piece as I am fairly new to Python/Django.
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
emp_num = form.cleaned_data['adp_number']
if 'enter_area' in self.request.POST:
form.save()
return HttpResponseRedirect(self.request.path_info)
elif 'leave_area' in self.request.POST:
form.save()
EmployeeWorkAreaLog.objects.filter(adp_number=emp_num).update(time_out=datetime.now())
return HttpResponseRedirect(self.request.path_info)
elif 'manager_options' in self.request.POST:
return redirect('enter_exit_area_manager_options_list')
class EnterExitAreaManagerOptionsList(ListView):
filter_form_class = EnterExitAreaManagerOptionsFilterForm
default_sort = "name"
template = "operations/list.html"
def get_initial_queryset(self):
return EmployeeWorkAreaLog.active.all()
def set_columns(self):
self.add_column(name='Employee #', field='adp_number')
self.add_column(name='Work Area', field='work_area')
self.add_column(name='Station', field='station_number')
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
url(r'enter-exit-area-manager-options-list/$', EnterExitAreaManagerOptionsList.as_view(), name='enter_exit_area_manager_options_list'),
]
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<div>
{{ form.adp_number.help_text }}
{{ form.adp_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
<div>
{{ form.station_number.help_text }}
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% endblock main %}
{% block panel_footer %}
<div class="text-center">
<button type="submit" name="manager_options" value="Options">
Manager Options
</button>
</div>
{% endblock panel_footer %}
list.html
{% extends "base.html" %}
{% load core_tags staticfiles %}
{% block head %}
<script src="{% static "js/operations/enter_exit_area_manager_options_list.js" %}"></script>
{% endblock head %}
{% block main %}
{% include 'core/list_view/list.html' %}
{% endblock main %}
You option buttons is really link to another page so you should add it to your template like this. Replacing button-styles class with however you want your button to look.
<a href="{% url 'enter_exit_area_manager_options_list' %}" class="button-styles">
Manager Options
</a>

Django class based view pagination not working

I am trying to use a pagination to paginate one of my pages. I have tried a few different methods I have found online but for some reason, when I use paginate_by = 3, it doesn't actually paginate anything, yet still shows the html for the pagination at the bottom of the page.
View:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
HTML:
{% extends 'public/base.html' %}
{% load staticfiles %}
{% block head %}
<link rel="stylesheet" type="text/css" href="{% static "public/css/search.css" %}" />
{% endblock %}
{% block content%}
<div class="search container-fluid">
<img src="/media/about-us.jpg" alt="">
<div class="search-title">
<h1 class="title">Search</h1>
</div>
<div class="search-main mb-5">
<form method='GET' action=''>
<input type="text" name='q' class="homebanner-search" placeholder="Enter your keywords" value='{{ request.get.q }}'>
</form>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="detail-container">
{% for post in queryset %}
<a href="{% url 'post-detail' post.slug %}">
<div class="post-main">
<div class="post-image">
<img src="{{ post.image.url }}" class="card-img-top" alt="#">
<p class="post-category">{{ post.category }}</p>
</div>
<div class="post-body">
<div class="post-title">
<p class="post-title-p">Day in the life of {{ post.title }}</p>
</div>
<div class="post-text">
<p class="post-author-text text-muted">{{ post.sub_description|truncatewords:22 }}</p>
</div>
<div class="post-button">
<p>READ MORE ></p>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
<div id="page_navigation" >
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
So on the page, 3 items should be showing, and pagination should take me to the the next set of 3. The html is showing, and when I click the links it is taking me 2 page 2. The problem is the fact that 6 items are showing, and not 3, and when I go to page 2, there are the same 6 items there.
Unfortunately I couldn't reproduce your error exactly, but what did happen is that my objects wouldn't render when looping through queryset. So what I would recommend is try to loop through object_list instead:
{% for post in object_list %}
{{ post.name }}
{% endfor %}
Another thing you can do is add a context_object_name argument to your view:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
context_object_name = 'posts'
and then loop through that:
{% for post in posts %}
{{ post.name }}
{% endfor %}
Also, I can't visualise what the search form is doing on this page since the ListView's model (Post) is the queryset, not whatever is searched for? So perhaps the link href is causing some trouble. Maybe try something like this instead:
<li>«</li>
Again, I could not reproduce the exact problem you are having, which is a shame because I feel like I have had the same issue before, so these are just suggestions pieced together from pagination that works for me and the code you posted. Hope it points you in the right direction.

Change BooleanField from False to True when message is opened

I've created my own message engine on Django framework to let users send messages to each others, here is my message model
models.py
class Message(models.Model):
sender = models.ForeignKey(UserModel, related_name="sender", on_delete='CASCADE')
receiver = models.ForeignKey(UserModel, related_name="receiver", on_delete='CASCADE')
msg_title = models.CharField(max_length=150, verbose_name='عنوان الرسالة', default='رسالة جديدة من مستخدم فوستانيا')
msg_content = models.TextField(max_length=1200,verbose_name='محتوى الرسالة')
created_at = models.DateTimeField(auto_now=True)
read = models.BooleanField(default=False)
Then am listing messages for the user, they can see the messages with read=False as a new message, they are able to click it to see the full message, I want the read status to be changed to True after the user clicks the message from the template,, How to do it!
urls.py
path('messages/', views.messages, name="messages"),
path('messages/<int:pk>/', views.message_page, name="message_page"),
views.py
#login_required
def messages(request):
inbox = Message.objects.filter(receiver=request.user, read=True)
context = {
'inbox': inbox,
}
return render(request, 'fostania_web_app/messages.html', context)
def message_page(request, pk):
current_msg = get_object_or_404(Message, pk=pk)
context = {
'current_msg': current_msg,
}
return render(request, 'fostania_web_app/message_page.html', context)
Message list template message.html
{% extends 'fostania_web_app/base.html' %}
{% block content %}
{% load static %}
{% if user.is_authenticated %}
<br><br>
<div class="card text-white bg-warning mb-3" style="max-width: 75rem;" align="right">
<div class="card-header">رسائل جديدة </div>
<div class="card-body">
<p class="card-text">
{% if new_messages.count != 0 %}
{% for msg in new_messages %}
<img src="{% static 'img/new-msg.png' %}"> {{ msg.msg_title }}
<br>
{% endfor %}
{% else %}
لا توجد رسائل غير مقروءة
{% endif %}
</p>
</div>
</div>
<!-- old msgs -->
<div class="card text-dark bg-ligh mb-3" style="max-width: 75rem;" align="right">
<div class="card-header"><img src="{% static 'img/inbox.png' %}"> صندوق الوارد </div>
<div class="card-body">
<p class="card-text">
{% for msg in inbox %}
<img src="{% static 'img/old-msg.png' %}"> {{ msg.msg_title }}<br>
{% endfor %}
</p>
</div>
</div>
{% else %}
يتوجب عليك تسجيل الدخول اولاً
{% endif %}
{% endblock %}
Message body after clicking and passing it's pk message_page.html
{% extends 'fostania_web_app/base.html' %}
{% block content %}
{% load static %}
<Br><br>
<div class="card bg-light mb-3" style="max-width: 50rem;" align="right ">
<div class="card-header">{{ current_msg.sender.name }}</div>
<div class="card-body">
<h5 class="card-title">{{ current_msg.msg_title }}</h5>
<p class="card-text">{{ current_msg.msg_content }}
<Br><br>
<button class="btn btn-success">إرسـال رد</button>
<button class="btn btn-danger">رجوع للرسائل </button>
</p>
</div>
</div>
{% endblock %}
You can avoid too much work by simply updating the message being read:
def message_page(request, pk):
current_msg = get_object_or_404(Message, pk=pk)
current_msg.read = True
current_msg.save()
context = {
'current_msg': current_msg,
}
return render(request, 'fostania_web_app/message_page.html', context)
BONUS:
instead of
{% if new_messages.count != 0 %}
{% for msg in new_messages %}
''' '''
{% endfor %}
{% else %}
لا توجد رسائل غير مقروءة
{% endif %}
Inside the loop, you can check whether the msg is read or not rather than send one more queryset, so you can simply do
{% for msg in new_messages %} # instead of new_messages, send all_messages
{% if msg.read %}
''' old message '''
{% else %}
''' new message '''
{% endif %}
{% empty %}
لا توجد رسائل غير مقروءة # don't really know what does that mean
{% endfor %}

Flask square brackets and single quotas at showing all validation message at once

I have created a very simple login form with flask
email = StringField('E-Mail:', validators=[validators.DataRequired(message='E-mail needed.'), validators.email(u'Provide e-mail.')])
password = StringField('Password:', [validators.required(u'Password can't be empty.')])
And the macro which will create the form inside the template:
{% macro render_field(field) %}
<fieldset>
<div class="form-group">
{{ field(**kwargs) | safe }}
</div>
</fieldset>
{% endmacro %}
As you can see it is very simple form. In the flask tutorial it was showing validation erors for individual fields. But I just want to show them all at once on the top of the form.
Here is my template for that:
<div class="panel-body">
{% if loginForm.errors %}
<div class="alert alert-danger">
<ul>
{% for error in loginForm.errors %}
<li>{{ loginForm.errors[error] }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form role="form" method="post" action="{{ url_for('login') }}">
{% from "_formhelper.html" import render_field %}
<fieldset>
{{ render_field(loginForm.email, class="form-control", placeholder="E-Mail") }}
{{ render_field(loginForm.password, class="form-control", placeholder="Password") }}
<button class="btn btn-lg btn-success btn-block">Login</button>
</fieldset>
</form>
</div>
As you can see, i placed my error section on the top of the form. But when
I post empty form, it shows the error messages like this:
['Password can't be empty'] instead of Password can't be empty
Why does it append square brackets and single quotas, start and end of the message? I don't know.
I know that this is an old question but if anyone else has this problem you can fix it by adding a for loop for the messages, something like this:
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning my-5">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}

Categories