Search code examples
htmldjangodjango-viewsbootstrap-5django-taggit

Tagged Posts not Paginating


I have a Django app that displays posts that are created using Django's built-in admin interface. The posts have tags incorporated using django-taggit (https://django-taggit.readthedocs.io/en/latest/)

The main page (home.html) is set up to display posts and tags and when a tag is clicked, it takes you to a page (tag_posts.html) with all tagged posts e.g. If i click on a post with the tag 'apple', I am presented with a page that shows all posts tagged with 'apple'. The main page works as intended, as does the pagination for home.html.

THE ISSUE: When viewing the list of tagged posts, it is showing the number of posts by the number specifed with paginate_by (in the code it's 2) but not showing me the option to click next/previous or page numbers.

What I Have Attempted:

  • I thought it may be the Bootstrap navigation links in the html file but I am using the same as in my home.html, which works.

  • re-factored my class-based view to include the tag as part of the context and supplying to the html as a context variable

  • Used basic html for navigation links

The issue lies with TagPostsView

Here is my view.py:

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse
from django.views.generic import ListView, TemplateView
from .models import Session, PostsInSession
from django.core.paginator import Paginator
from taggit.models import Tag



class PostView(ListView):

    queryset = Session.objects.prefetch_related('postsinsession_set').all()
    context_object_name = 'sessions'
    template_name = 'home.html'
    paginate_by = 2  # Number of items per page

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        tags = Tag.objects.order_by('name')
        paginator = Paginator(self.queryset, self.paginate_by)
        page = self.request.GET.get('page')
        sessions = paginator.get_page(page)
        context['sessions'] = sessions
        context['tags'] = tags
        return context




class TagPostsView(ListView):
    template_name = 'tag_posts.html'
    context_object_name = 'posts'
    paginate_by = 2

    def get_queryset(self):
        tag_slug = self.get_tag_slug()
        posts = PostsInSession.objects.filter(post__tags__slug=tag_slug)
        return posts

    def get_tag_slug(self):
        return self.kwargs['tag_slug']

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        tag_slug = self.get_tag_slug()
        tag = get_object_or_404(Tag, slug=tag_slug)
        paginator = Paginator(context['posts'], self.paginate_by)
        page = self.request.GET.get('page')
        paginated_posts = paginator.get_page(page)
        context['tag'] = tag
        context['posts'] = paginated_posts
        return context

tag_posts.html:


{% extends "base.html" %}
{% load markdownify %}

{% block content %}
<div class="row">
  <div class="col">
    <br>
    {% if tag %}
      <h2>Tag: {{ tag.name }}</h2>
    {% endif %}
    <hr>
    {% for post in posts %}
    <div class="card m-3 text-center">

      <div class="card-header">
        {{ post.post.title }}
      </div>

      <div class="card-body">
        {{ post.post.body|markdownify }}
      </div>

    </div>
    {% endfor %}
    <nav aria-label="Page navigation">
      <ul class="pagination justify-content-center">
        {% if posts.has_previous %}
          <li class="page-item">
            <a class="page-link" href="?page={{ posts.previous_page_number }}" aria-label="Previous">
              <span aria-hidden="true">&laquo;</span>
            </a>
          </li>
        {% endif %}
        
        {% for num in posts.paginator.page_range %}
          {% if posts.number == num %}
            <li class="page-item active" aria-current="page">
              <span class="page-link">{{ num }}</span>
            </li>
          {% else %}
            <li class="page-item">
              <a class="page-link" href="?page={{ num }}">{{ num }}</a>
            </li>
          {% endif %}
        {% endfor %}
        
        {% if posts.has_next %}
          <li class="page-item">
            <a class="page-link" href="?page={{ posts.next_page_number }}" aria-label="Next">
              <span aria-hidden="true">&raquo;</span>
            </a>
          </li>
        {% endif %}
      </ul>
    </nav>
  </div>
</div>
{% endblock %}


Solution

  • I found the solution in the end.

    The paginator instance in get_context_data() of the TagPostsView should take the queryset as opposed to the context['posts'].

    This is because the Paginator class takes the queryset and splits into page objects (see: https://docs.djangoproject.com/en/4.2/topics/pagination/).

    My original code was paginating the already paginated posts. Here is the fixed code:

        
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            tag_slug = self.get_tag_slug()
            tag = get_object_or_404(Tag, slug=tag_slug)
            paginator = Paginator(self.get_queryset(), self.paginate_by)
            page = self.request.GET.get('page')
            paginated_posts = paginator.get_page(page)
            context['tag'] = tag
            context['posts'] = paginated_posts
            return context