Kamruzzaman Tauhid
Kamruzzaman Tauhid

Reputation: 167

Is there any way to use Pagination with django-filter?

Whenever I press on a link to the next page (which is a get request), the filter is bypassed and I get the corresponding page for the entire list (without the filter). Example, if I am on page 1 for a filtered list and I click next, I land on page 2 for the entire unfiltered list. In models.py,

class Person(models.Model):
    name = models.CharField(max_length=50, unique=True)
    gender = models.CharField(max_length=7, choices=GENDER_CHOICES)
    category = models.CharField(max_length=20, choices=get_all_category_choices())

In filters.py,

import django_filters

from .models import Person

class PersonFilter(django_filters.FilterSet):

    class Meta:
        model = Person
        fields = [
            'name',
            'gender',
            'category',
        ]

In views.py,

def show_all_persons_page(request):
    context = {}

    filtered_person_list = PersonFilter(
        request.GET,
        queryset=Person.objects.all()
    )

    context['filtered_person_list'] = filtered_person_list

    paginated_filtered_person_list = Paginator(filtered_person_list.qs, 3)
    page_number = request.GET.get('page')
    person_page_obj = paginated_filtered_person_list.get_page(page_number)

    context['person_page_obj'] = person_page_obj

    return render(request, 'app1/show_all_persons_page.html', context)

In app1/show_all_persons_page.html,

<form method="get">
    {{ filtered_person_list.form.as_p }}
    <button type="submit">Search</button>
</form>

{% for person in person_page_obj %}
    <img src="{{ person.picture.url }}" width="240">
    <h2>
        {{person.name}}
        <br>
        {{person.gender}}
        <br>
        {{person.category}}
        <br>
    </h2>
    <br>
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if person_page_obj.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ person_page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ person_page_obj.number }} of {{ person_page_obj.paginator.num_pages }}.
        </span>

        {% if person_page_obj.has_next %}
            <a href="?page={{ person_page_obj.next_page_number }}">next</a>
            <a href="?page={{ person_page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

Upvotes: 0

Views: 285

Answers (1)

Kamruzzaman Tauhid
Kamruzzaman Tauhid

Reputation: 167

Solved. Concept achieved from https://simpleisbetterthancomplex.com/snippet/2016/08/22/dealing-with-querystring-parameters.html. In order to retain the current url parameters, we have to define a custom template tag. Create a new directory called templatetags under app1. Create a __init__.py inside that directory. Create another .py file, say app1_extras. Inside app1_extras, define a custom template tag as follows. app1/templatetags/app1_extras.py,

from django import template

register = template.Library()

@register.simple_tag
def relative_url(value, field_name, urlencode=None):
    url = '?{}={}'.format(field_name, value)
    if urlencode:
        querystring = urlencode.split('&')
        filtered_querystring = filter(lambda p: p.split('=')[0] != field_name, querystring)
        encoded_querystring = '&'.join(filtered_querystring)
        url = '{}&{}'.format(url, encoded_querystring)
    return url

This will get our current url and ensure that the parameters are in place along with the page number. field_name is the string 'page' and value is the page number that will be passed from the template. urlencode represents our current url as a string. In app1/show_all_persons.html,

{% load app1_extras %}

<form method="get">
    {{ filtered_person_list.form.as_p }}
    <button type="submit">Search</button>
</form>

{% for person in person_page_obj %}
    <img src="{{ person.picture.url }}" width="240">
    <h2>
        {{person.name}}
        <br>
        {{person.gender}}
        <br>
        {{person.category}}
        <br>
    </h2>
    <br>
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if person_page_obj.has_previous %}
            <a href="{% relative_url 1 'page' request.GET.urlencode %}">&laquo; first</a>
            <a href="{% relative_url person_page_obj.previous_page_number 'page' request.GET.urlencode %}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ person_page_obj.number }} of {{ person_page_obj.paginator.num_pages }}.
        </span>

        {% if person_page_obj.has_next %}
            <a href="{% relative_url person_page_obj.next_page_number 'page' request.GET.urlencode %}">next</a>
            <a href="{% relative_url person_page_obj.paginator.num_pages 'page' request.GET.urlencode %}">last &raquo;</a>
        {% endif %}
    </span>
</div>

{% load app1_extras %} will load our new custom template tag. relative_url function is now passed to the hrefs for our page links. The arguments are passed in order where request.GET.urlencode will contain our current url and will be passed to the urlencode parameter.

Upvotes: 1

Related Questions