Change BooleanField from False to True when message is opened - python

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 %}

Related

Pagination in Django ListView when using get_context_data

I am currently facing this problem with Django ListView. Basically, I need to filter some questions per topic and I would like to paginate the results.
My code is working perfectly about the queryset part (the results are showed correctly) but I am facing a problem with pagination.
Let's say I have so far 8 items in my query, if I choose to paginate_by = 10, it shows me just one page. If, otherwise, I choose to paginate by, let's say, 3, it shows me 3 pages to choose in the template (which is correct) but it shows me ALL the results of the query in my page.
I post some code to be more clear
models.py:
class Tag(models.Model):
name = models.CharField(max_length=300, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
self.name = self.name.capitalize()
def __str__(self):
return self.name
class Question(models.Model):
post_owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=5000, default='')
body = tinymce_models.HTMLField()
tags = models.ManyToManyField(
Tag, related_name='tags')
viewers = models.ManyToManyField(
User, related_name='viewed_posts', blank=True)
views.py:
class TagQuestionsListView(ListView):
template_name = 'main/tag_questions.html'
paginate_by = 20
def get_queryset(self, **kwargs):
tag = Tag.objects.get(name=self.kwargs['name'])
questions = Question.objects.filter(tags=tag)
return questions
def get_contextdata(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tag'] = Tag.objects.get(name=self.kwargs['name'])
context['questions'] = Question.objects.filter(
tags=context['tag'], is_deleted=False)
return context
template:
{% extends 'base.html' %}
{% load humanize %}
{% block title %}Domande su {{tag.name}}{% endblock title %}
{% block content %}
<style>
.text {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2; /* number of lines to show */
line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>
<div class="card">
<div class="mt-3">
<h3 class="ms-2" style="display: inline;">Ultime {{ questions.count }} Domande</h3>
{% if request.user.is_authenticated %}
Fai una domanda
{% else %}
Fai una domanda
{% endif %}
</div>
<h5 class="ms-2">{{ total_questions.count }} domande totali </h5>
<hr>
{% for question in questions %}
<div class="row">
<div class="col-2 ms-2">
<p>{{ question.count_all_the_votes }} Voti</p>
<p>{{ question.count_answers }} Risposte</p>
<p>{{ question.calculate_viewers }} Visualizzazioni</p>
</div>
<div class="col-9">
<h5>{{question.title}}</h5>
<div class="text">
{{ question.body|striptags }}
</div>
{% for tag in question.tags.all %}
<a class="btn btn-sm btn-outline-primary m-1" href="{% url 'main:tag_questions' name=tag.name %}">{{tag.name}}</a>
{% endfor %}
<div class="float-end">
<a style="display: inline;" href="">{{question.post_owner}}</a>
{% if question.is_edited == False %}
<span style="display:inline;">creata {{question.created_at | naturaltime}}</span>
{% else %}
<span style="display:inline;"> modificata {{question.edited_time | naturaltime}} da <a style="display: inline;" href="">{{question.edited_by.username}}</a></span>
{% endif %}
</div>
</div>
</div>
<hr>
{% endfor %}
</div>
<div class="row mt-3">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="{% url 'main:tag_questions' name=tag.name %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.previous_page_number }}">Precedente</a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#">Precedente</a></li>
{% endif %}
{% for i in paginator.page_range %}
<li class="page-item {% if page_obj.number == i%} active {% endif %} %} "><a class="page-link" href="?filter={{ filter }}&orderby={{ orderby }}&page={{ i }}">{{i}}</a></li>
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="{% url 'main:tag_questions' name=tag.name %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.next_page_number }}">Prossima</a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#">Prossima</a></li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
{% endblock content %}
With this I want my comment converter to answer.
You have to use page_obj instead questions in your template.
<--! ⬇️⬇️⬇️ -->
{% for question in page_obj %}
<div class="row">
<div class="col-2 ms-2">
<p>{{ question.count_all_the_votes }} Voti</p>
<p>{{ question.count_answers }} Risposte</p>
<p>{{ question.calculate_viewers }} Visualizzazioni</p>
</div>
<div class="col-9">
<h5>{{question.title}}</h5>
<div class="text">
{{ question.body|striptags }}
</div>
{% for tag in question.tags.all %}
<a class="btn btn-sm btn-outline-primary m-1" href="{% url 'main:tag_questions' name=tag.name %}">{{tag.name}}</a>
{% endfor %}
<div class="float-end">
<a style="display: inline;" href="">{{question.post_owner}}</a>
{% if question.is_edited == False %}
<span style="display:inline;">creata {{question.created_at | naturaltime}}</span>
{% else %}
<span style="display:inline;"> modificata {{question.edited_time | naturaltime}} da <a style="display: inline;" href="">{{question.edited_by.username}}</a></span>
{% endif %}
</div>
</div>
</div>
<hr>
{% endfor %}
So, if you define attribute paginate_by , than ListView(django docs) adds a paginator and page_obj to the context. To allow the users to navigate between pages, add links to the next and previous page, in your template.

Trying to show model data on web page by using ListView. Its not working

I created the model post and I want to show all the posts on Web Page using Class-based views but It's not working. the URL is opening but the web page is not showing anything( stuff that is on homepage.html and on _post.html but navbar is there which is coming from _inject.html). The problem is in coding in the template.
Post Model-
class Post(models.Model):
auther = models.ForeignKey(User, related_name="posts",on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=True)
heading = models.CharField(max_length=400)
message = models.TextField()
message_html = models.TextField(editable=False)
def __str__(self):
return self.message
def save(self,*args,**kwargs):
self.message_html = misaka.html(self.message)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('posts:single',kwargs={'username':self.user.username,'pk':self.pk})
class Meta:
ordering = ['-created_at']
unique_together = ['auther','message']
Post view-
class ListPosts(generic.ListView):
model = models.Post
template_name = "homepage.html"
homepage.html
{% extends "_inject.html" %}
{% block content %}
<div class="col-md-8">
{% if post.count == 0 %}
<h2>No posts in this group yet!</h2>
{% else %}
<ul>
{% for p in Post.all %}
<h1>p.message</h1>
<li> {% include "_post.html" %} </li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endblock %}
_post.html-
<div class="media">
<h3 class="mr-5">#{{ post.user.username }}</h3>
<div class="media-body">
<strong>{{ p.user.username }}</strong>
<h5>{{ p.message_html|safe }}</h5>
<time class="time">{{ p.created_at }}</time>
<div class="media-footer">
{% if user.is_authenticated and post.user == user and not hide_delete %}
<a href="{% url 'posts:delete' pk=post.pk %}" title="delete" class="btn btn-simple">
<span class="fa fa-remove text-danger" aria-hidden="true"></span>
<span class="text-danger icon-label">Delete</span>
</a>
{% endif %}
</div>
</div>
</div>
Try changing your homepage.html to something like this:
{% extends "_inject.html" %}
{% block content %}
<div class="col-md-8">
{% if post_list %}
{% for post in post_list %}
<h1>{{post.message}}</h1>
<li>{% include "_post.html" %} </li>
{% endfor %}
{% else %}
<h2>No posts in this group yet!</h2>
{% endif %}
</div>
{% endblock %}

Displaying a message as a popup/alert in Django?

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 %}

How to loop though user objects in django

I want to show all the user's Items.
Currently I can only show 1 object
my_items.html (where i want it to show)
{% extends 'base.html' %}
{% block content%}
<main role="main">
<div class="container">
<!-- Example row of columns -->
<div class="row">
{% for item in item %}
<!--{% if item.id == request.user.id %}-->
<div class="col-md-4">
<div class="col-md-4">
<img style="max-width: 100px; max-height: 300px" src="{{ item.thumb.url }}">
</div>
<h2>{{ item.name }}</h2>
<p>{{ item.snippet }}</p>
<p>{{ item.date }}</p>
<p><a class="btn btn-warning" href="#" role="button">Edit</button></a></p>
<p><a class="btn btn-danger" href="#" role="button">Delete</a></p>
</div>
<!--{% endif %}-->
{% endfor %}
</div>
<hr>
</div> <!-- /container -->
</main>
{% endblock %}
views.py
def item_myitems(request):
item = Item.objects.all().order_by('date')
return render(request, 'items/my_items.html', {'item': item})
I tried using filter() and get() on views.py
You should use a plural name to pass your items to your view:
def item_myitems(request):
items = Item.objects.all().order_by('date')
return render(request, 'items/my_items.html', {'items': items})
So you can distinguish one item from many items when you loop through them in your view:
{% for item in items %}
omg I got it working, i feel so dumb right now LOL
I uncommented my if in the html and set to this > {% if item.author_id == request.user.id %} i was comparing with the item id instead of the author_id

i am getting an error saying category matching query does not exist

The voting proceess is working fine with this code. The problem is only when redirecting after voting the options.
Exception Type:DoesNotExist
Exception Value:
Category matching query does not exist.
category = Category.objects.get(slug=slug)
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/vote/', views.vote, name='vote'),
views.py
def home(request):
categories = Category.objects.filter(active=True)
return render(request,'rank/base.html',{'categories': categories,'title':'TheRanker'})
def options(request,slug):
category = Category.objects.get(slug=slug)
options = Option.objects.filter(category=category)
return render(request,'rank/options.html',{'options':options,'title':'options'})
def vote(request,slug):
option = Option.objects.get(slug=slug)
if Vote.objects.filter(slug=slug,voter_id=request.user.id).exists():
messages.error(request,'You Already Voted!')
return redirect('rank:options',slug)
else:
option.votes += 1
option.save()
voter = Vote(voter=request.user,option=option)
voter.save()
messages.success(request,'Voted!')
return redirect('rank:options',slug)
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<center>{% bootstrap_messages %}</center>
<ol type="1">
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="200" height="100">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% endfor %}
</ol>
</center>
{% endblock content%}
You're confusing categories and options. The form sends the slug of the option, but then you redirect to the categories view using the same slug. But those are two different models.

Categories