modify the value of paginate_by for all pages

Viewed 238

I have a follow-up question on the answer to Change value for paginate_by on the fly

I added the following in the HTML

<form method="GET">
   <select name="paginate_by" id="">
        <option value="5">5</option>
        <option value="10">10</option>
        <option value="20">20</option>
        <option value="50">50</option>
   </select>
   <input type="submit" value="Paginate">
</form>

and this function in my ListView class

class ReviewPostListView(ListView):
    model = Reviews
    template_name = 'reviews/reviews.html'
    context_object_name = 'rows'
    ordering = ['id']
    paginate_by = 5

    def get_paginate_by(self, queryset):
        return self.request.GET.get("paginate_by", self.paginate_by)

it is working great, and the paginate_by is added to the URL. My problem is when I click on the second page it goes back to 5.

This is my pagination HTML

{% if is_paginated %}
    {% if page_obj.has_previous %}
        <a class="btn btn-outline-info mb-4" href="?page=1">First</a>
        <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a>
    {% endif %}
    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <a class="btn btn-info mb-4" href="?page={{ num }}">{{num}}</a>
        {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
            <a class="btn btn-outline-info mb-4" href="?page={{ num }}">{{num}}</a>
        {% endif %}
    {% endfor %}
    {%if page_obj.has_next %}
        <a class="btn btn-outline-info mb-4" href="?page={{page_obj.next_page_number}}">Next</a>
        <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
    {% endif %}
{% endif %}

What is the pythonic of keeping the paginate_by (if exists) parameter in the URL when looping on the pages?

3 Answers

You can try this in your HTML template:

<select name="paginate_by" onchange="location = this.value;">
      <option value="">select</option>
      <option value="?paginate_by=5&{{page_obj.number}}">5</option>
      <option value="?paginate_by=10&{{page_obj.number}}">10</option>
      <option value="?paginate_by=20&{{page_obj.number}}">20</option>
      <option value="?paginate_by=50&{{page_obj.number}}">50</option>
</select>

Pass the paginate_by object into the html and add it to the pagination links. see below code:

{% if is_paginated %}
    {% if page_obj.has_previous %}
        <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page=1">First</a>
        <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page={{ page_obj.previous_page_number }}">Previous</a>
    {% endif %}
    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <a class="btn btn-info mb-4" href="?paginate_by={{ paginate_by}}&page={{ num }}">{{num}}</a>
        {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
            <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page={{ num }}">{{num}}</a>
        {% endif %}
    {% endfor %}
    {%if page_obj.has_next %}
        <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page={{page_obj.next_page_number}}">Next</a>
        <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page={{ page_obj.paginator.num_pages }}">Last</a>
    {% endif %}
{% endif %}

Using {{ request.GET.paginate_by }}, you can access the paginate_by query parameters in your template.

So, your pagination HTML will look like:

{% if is_paginated %}
  {% firstof request.GET.paginate_by "5" as paginate_by %}
    {% if page_obj.has_previous %}
        <a class="btn btn-outline-info mb-4" href="?page=1&paginate_by={{ paginate_by }}">First</a>
        <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a>
    {% endif %}
    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <a class="btn btn-info mb-4" href="?page={{ num }}">{{num}}</a>
        {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
            <a class="btn btn-outline-info mb-4" href="?page={{ num }}">{{num}}</a>
        {% endif %}
    {% endfor %}
    {%if page_obj.has_next %}
        <a class="btn btn-outline-info mb-4" href="?page={{page_obj.next_page_number}}&paginate_by={{ paginate_by }}">Next</a>
        <a class="btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}&paginate_by={{ paginate_by }}">Last</a>
    {% endif %}

{% endif %}

firstof is will set the value of paginate_by to the query parameter or 5, which will be the default value.

You can create a template tag which will handle the query params for you.

Here are two simple tags

@register.simple_tag(takes_context=True)
def add_get_param(context, **kwargs):
    "Used to add/replace query parameters to the current URL."
    params = context["request"].GET.dict()
    params.update(kwargs)
    return "?{}".format(urlencode(params))


@register.simple_tag(takes_context=True)
def remove_get_param(context, *args):
    "Used to remove query parameters from the current URL."
    params = context["request"].GET.dict()
    for key in args:
        params.pop(key, None)
    return "?{}".format(urlencode(params))

And your HTML will look like

{% if is_paginated %}
    {% if page_obj.has_previous %}
        <a class="btn btn-outline-info mb-4" href="?paginate_by={% add_get_param page=1 %}">First</a>
        <a class="btn btn-outline-info mb-4" href="{% add_get_param page=page_obj.previous_page_number %}">Previous</a>
    {% endif %}
    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <a class="btn btn-info mb-4" href="{% add_get_param page=num %}">{{num}}</a>
        {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
            <a class="btn btn-outline-info mb-4" href="?paginate_by={{ paginate_by}}&page={{ num }}">{{num}}</a>
        {% endif %}
    {% endfor %}
    {%if page_obj.has_next %}
        <a class="btn btn-outline-info mb-4" href="{% add_get_param page=page_obj.next_page_number %}">Next</a>
        <a class="btn btn-outline-info mb-4" href="{% add_get_param page={{ page_obj.paginator.num_pages %}">Last</a>
    {% endif %}
{% endif %}

With this whatever was already there in the query params will continue to be there, only the page will be changed. So this works if you decide to add more params such as for filtering.

Related