Django DetailView pagination no navigation numbers
(Yes, I know that the first suggestion will be to convert my DetailView into ListView; I've tried that, and it wasn't helpful)
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've looked through some tutorials and questions here in SOF, 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.
Please help me.
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}.'
The {% if is_paginated %}
is outside the {% endblock %}
, so it will not render, but even if it was inside the block, it will not work with the DetailView
, since it did not pass that to the context.
Another problem is that your .get_context_data(…)
[Django-doc] does not return the items of the underlying .get_context_data(…)
so essentially nullifying it.
I would advise to work with a ListView
:
class ShowTopicView(ListView):
model = Comment
template_name = 'forum/topic.html'
paginate_by = 5
def get_queryset(self, *args, **kwargs):
return (
super()
.get_queryset(*args, **kwargs)
.filter(topic__slug=self.kwargs['topic_slug'])
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
context = {**context, 'menu': menu, 'topic': topic}
return context
and the {% if is_paginated %}
should be put in a block, not per see the block where you are rendering the comments, but a block defined by the "parent" template.