Search code examples
djangopaginationdjango-querysetdjango-class-based-viewsdjango-pagination

Pagination using ListView and a dynamic/filtered queryset


I have a class based view that I am using to display a queryset in a table. I am also using a couple formsets to filter this queryset. I am using the get_queryset() method provided as part of the generic.ListView class to filter the diplayed results. Here is basically what my class looks like:

from django.views import generic

class UnifiedSingleSearch(generic.ListView):
    template_name = 'app/foo.html'
    model = MyModel
    paginate_by = 30

    def get_queryset(self):
        if self.request.POST:  # If we got here because of a search submission, filter
            return MyModel.objects.filter('Some stuff base on the POST data')
        return MyModel.objects.all()  # Otherwise, just show everything

Because I am using a formset to submit multiple search criteria, I have to use a POST request. Upon initial submission of the form, the page reloads with a correctly filtered querset. However when I try to use my pagination controls, the POST request is thrown away and the page acts as if I am going to page#2 of MyModel.objects.all() instead of my filtered down subset.

How can I retain my filtered queryset when using pagination controls?

Here is the HTML for the pagination controls:

{% if is_paginated %}
        <nav aria-label="Pagination nav">
            <ul class="pagination">
                {# Back a page #}
                {% if page_obj.has_previous %}
                    <li class="page-item">
                        <a class="page-link" href="?page={{ page_obj.previous_page_number }}">&#10094;</a>
                    </li>
                {% else %}
                    <li class="page-item disabled">
                        <span class="page-link">&#10094;</span>
                    </li>
                {% endif %}
                {# Page numbers #}
                {% for i in paginator.page_range %}
                    {% if page_obj.number == i %}
                        <li class="page-item active">
                            <span class="page-link">{{ i }}
                                <span class="sr-only">(current)</span>
                            </span>
                        </li>
                    {% else %}
                        <li class="page-item">
                            <a class="page-link" href="?page={{ i }}">{{ i }}</a>
                        </li>
                    {% endif %}
                {% endfor %}
                {# Next page #}
                {% if page_obj.has_next %}
                    <li class="page-item">
                        <a class="page-link" href="?page={{ page_obj.next_page_number }}">&#10095;</a>
                    </li>
                {% else %}
                    <li class="page-item disabled">
                        <span class="page-link">&#10095;</span>
                    </li>
                {% endif %}
            </ul>
        </nav>
    {% endif %}
{% else %}
    <p>No MyModel objects found</p>
{% endif %}

Solution

  • Switching to a GET request was the answer. Pagination controls also had to be edited a bit to retain the passed querystring. They should all include {{ request.GET.urlencode }} and then you tack on the page logic to the end like usual. Ends up looking something like this:

    <li class="page-item">
        <a class="page-link" href="?{{ request.GET.urlencode }}&page={{ page_obj.next_page_number }}">&#10095;</a>
    </li>