I have a Django forum app, its structure is “forum → subforum → topic → comments”. Comments are displayed on their related topic’s page, so a topic’s view handles Topic together with its comments. Topic itself includes its subject and the initial comment (first_comment
). I purposefully use DetailView here instead of ListView, as 1) I want to slve the problem this way; 2) I’ve tried to change it to ListView, but it didn’t help.
I’ve looked through some tutorials and earlier questions, so somehow I’ve made my pagination work in the sense that it cuts the amount of displayed comments by the number in the parameter in Paginator
(in the example it’s 5). But, unfortunately, I cannot reproduce any links, numbers of pages to navigate through the pagination. And I don’t even know, where is the cause of this problem hidden - either in html code, either in the view’s code.
Asking for help and advice of the community.
View:
class ShowTopic(DetailView):
model = Topic
template_name = "forum/topic.html"
slug_url_kwarg = 'topic_slug'
context_object_name = 'topic'
def get_context_data(self, **kwargs):
topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
comments = self.get_comments(topic)
comments_number = len(Comment.objects.filter(topic__id=topic.id))
context = {'menu': menu,
'topic': topic,
#'comments': comments,
'page_obj': comments,
'comm_num': comments_number}
return context
def get_comments(self, topic):
qs = Comment.objects.filter(topic=topic)
paginator = Paginator(qs, 5)
page = self.request.GET.get('page')
comments = paginator.get_page(page)
return comments
topic.html (the template of a topic and its comments)
{% extends 'base.html' %}
{% load static %}
{% block content %}
<h1>{{ topic.subject }}</h1>
<div class="container-posts">
<div class="row">
<div class="card-subtitle">Created by: {{ topic.creator }}, time: {{ topic.created }}</div>
<div class="card-subtitle">Number of comments in the topic: {{ comm_num }}</div>
<div class="container-posts">
<a href="add_comment"><button>Leave a comment</button></a>
</div>
<div class="card-body">
<div class="media">
{% if p.photo %}
<img src="{{p.photo.url}}" alt="" width="300" height="300">
{% else %}
<img src="{% static 'core/images/Swr-portrait-aya.png' %}" alt="" width="150" height="150">
{% endif %}
</div>
<h5 class="card-subtitle">{{ topic.creator }}</h5>
<h5 class="card-subtitle">Created: {{topic.created|date:"d.m.Y H:i"}}</h5>
{% autoescape off %}
<p class="card-text">{{topic.first_comment|linebreaks|truncatewords:200}}</p>
{% endautoescape %}
<a href="{% url 'forum:edit_comment' topic.subforum.slug topic.slug topic.id %}">Edit</a>
<div class="clear"></div>
<hr>
</div>
<!--Comments section -->
{% for p in page_obj %}
<div class="card-posts">
<div class="card-body">
<div class="media">
{% if p.photo %}
<img src="{{p.photo.url}}" alt="" width="300" height="300">
{% else %}
<img src="{% static 'core/images/Swr-portrait-aya.png' %}" alt="" width="150" height="150">
{% endif %}
</div>
<h5 class="card-subtitle">{{ p.author }}</h5>
<h5 class="card-subtitle">Created: {{p.created|date:"d.m.Y H:i"}}</h5>
{% autoescape off %}
<p class="card-text">{{p.content|linebreaks|truncatewords:200}}</p>
{% endautoescape %}
<div class="card-subtitle">
<a href="{% url 'forum:edit_comment' topic.subforum.slug topic.slug p.id %}">Edit</a>
<a href="{% url 'forum:delete_comment' topic.subforum.slug topic.slug p.id %}">Delete</a></div>
<div class="clear"></div>
<hr>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{% if is_paginated %}
{% if page_obj.has_other_pages %}
<div class="pagination">
<ul>
{% if page_obj.has_previous %}
<li><a href="?page=1">« first</a></li>
<li><a href="?page={{ page_obj.previous_page_number }}"><</a></li>
{% endif %}
</ul>
<ul>
{% for p in paginator.page_range %}
{% if page_obj.number == p %}
<li class="page-num page-num-selected">{{ p }}</li>
{% elif page_obj.number|add:-1 and page_obj.number|add:1 %}
<li class="page-num">
<a href="?page={{ p }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
<ul>
{% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}">></a></li>
<li><a href="?page={{ page_obj.paginator.num_pages }}">last »</a></li>
{% endif %}
</ul>
</div>
{% endif %}
{% endif %}
urls.py:
from django.urls import path
from forum.views import *
app_name = 'forum'
urlpatterns = [
path('', SubForumListView.as_view(), name='forum'),
path('<slug:subforum_slug>/', TopicListView.as_view(), name='subforum'),
path('<slug:subforum_slug>/add_topic/', AddTopic.as_view(), name="add_topic"),
path('<slug:subforum_slug>/topics/<slug:topic_slug>/', ShowTopic.as_view(), name='topic'),
path('<slug:subforum_slug>/topics/<slug:topic_slug>/add_comment/', AddComment.as_view(), name="add_comment"),
path('<slug:subforum_slug>/topics/<slug:topic_slug>/<int:pk>/edit_comment/', UpdateComment.as_view(), name="edit_comment"),
path('<slug:subforum_slug>/topics/<slug:topic_slug>/<int:pk>/delete_comment/', DeleteComment.as_view(), name="delete_comment"),
]
Topic and Comment models:
class Topic(models.Model):
subject = models.CharField(verbose_name='Заголовок', max_length=255, unique=True)
first_comment = models.TextField(verbose_name='Сообщение', max_length=2000, default='')
slug = models.SlugField(default='', unique=True, max_length=25, editable=False)
subforum = models.ForeignKey('Subforum',
verbose_name='Раздел',
on_delete=models.CASCADE,
related_name='subforum')
creator = models.ForeignKey(User,
verbose_name='Создатель темы',
on_delete=models.SET('deleted'),
related_name='creator')
created = models.DateTimeField(auto_now_add=True)
closed = models.BooleanField(default=False)
objects = models.Manager()
class Meta:
ordering = ['id']
verbose_name = 'Обсуждения'
verbose_name_plural = 'Обсуждения'
def __str__(self):
return self.subject
def save(self, *args, **kwargs):
if not self.id:
self.slug = f'topic-{slugify(self.subject)}'
return super(Topic, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('forum:topic', kwargs={'topic_slug': self.slug, 'subforum_slug': self.subforum.slug})
class Comment(models.Model):
topic = models.ForeignKey('Topic',
verbose_name='Тема',
on_delete=models.CASCADE,
related_name='comments')
author = models.ForeignKey(User,
verbose_name='Комментатор',
on_delete=models.SET('deleted'),
related_name='author')
content = models.TextField(verbose_name='Текст', max_length=2000)
created = models.DateTimeField(verbose_name='Дата публикации', auto_now_add=True)
updated = models.DateTimeField(verbose_name='Дата изменения', auto_now=True)
objects = models.Manager()
class Meta:
ordering = ['created']
verbose_name = 'Комментарии'
verbose_name_plural = 'Комментарии'
def __str__(self):
return f'Post of {self.topic.subject} is posted by {self.author.username}.'