6

I have a filter with a dependent drop down for cars makes and models. Since I don't want to display all of them on one page I added a paginator. The issue is the filter works correctly but it does not carry over in the pages

when the filter is active the url looks like

/cars/?manufacture=2&model=2 If i go to the next page all I get is /cars/?page=2

I want something like /cars/?manufacture=2&model=2?page=2

If I print {{ posts|length }} it does return the proper number of items that are being filtered so there is not issue there

I believe the issue is with the next and previous buttons in the template as they don't pass any parameters in them other then next page. How do i carry the filter into the paginator.

view

def allCarsView(request):

    model = Post
    
    myFilter = carFilter(request.GET, queryset=Post.objects.all())
    posts = myFilter.qs
    
    page = request.GET.get('page', 1)
    paginator = Paginator(posts.order_by('date_posted'), 2)
    page_obj = paginator.get_page(page)
    page_range = paginator.get_elided_page_range(number=page)
    
    context = {
        'posts':posts, 'myFilter':myFilter, 'page_range': page_range, 
        'page': page, 'paginator': paginator, 'page_obj': page_obj
    }
    return render(request, 'blog/cars.html', context)    

Paginator html

<nav aria-label="Page navigation example " class="paginator">
<nav aria-label="Page navigation example " class="paginator">
    <ul class="pagination justify-content-center">
        <li class="page-item">
            {% if page_obj.has_previous %}
            <a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
            {% else %}
        </li>

        <li class="page-item disabled">
            <a class="page-link" href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        {% endif %} {% for i in page_obj.paginator.page_range %} {% if page_obj.number == i %}
        <li class="page-item active" aria-current="page">
            <a class="page-link" href="#">{{ i }}</a>
        </li>
        {% elif i > page_obj.number|add:'-3' and i < page_obj.number|add:'3' %}
        <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
        {% endif %} {% endfor %}

        <li class="page-item">
            {% if page_obj.has_next %}
            <a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Previous">
                <span aria-hidden="true">&raquo;</span>
            </a>
            {% else %}
        </li>

        <li class="page-item disabled">
            <a class="page-link" href="#" aria-label="Previous">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
        {% endif %}
    </ul>
</nav>

Update: From doing more research it does seem that my issue is with the urls in the paginator as they do not carry any parameters. There does not seem to be a best way to do this and the solutions I have tried have yielded nothing.

Update: Attempting to use this post as the solution

view (added under previous code) from django import template register = template.Library()

@register.simple_tag
def url_replace(request, field, value):

    dict_ = request.GET.copy()

    dict_[field] = value

    return dict_.urlencode()

template:

<a class="page-link" href="?{% url_replace request 'page' page_obj.next_page_number %}" aria-label="Previous">

I then get the error

Invalid block tag on line 88: 'url_replace', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?

so at the top if I add

{% load url_replace %}

throws the error

'url_replace' is not a registered tag library

Attempt at being able to select page number from paginator

            {% elif i > page_obj.number|add:'-3' and i < page_obj.number|add:'3' %}
            {% if page_obj.number > i  %}
                <li class="page-item"><a class="page-link" href="?{% querystring_replace request 'page' page_obj.previous_page_number %}">{{ i }}</a></li>    
            {% endif %}
            {% if page_obj.number < i  %}
                <li class="page-item"><a class="page-link" href="?{% querystring_replace request 'page' page_obj.next_page_number %}">{{ i }}</a></li>    
            {% endif %}
        {% endif %} {% endfor %}
Wazy
  • 462
  • 4
  • 17
  • 1
    Does this answer your question: [How to paginate Django with other get variables?](https://stackoverflow.com/questions/2047622/how-to-paginate-django-with-other-get-variables) – xyres Sep 13 '21 at 15:49
  • @xyres I tried to implement that post but couldnt get it to work. Its a very old post – Wazy Sep 13 '21 at 16:33
  • What's not working? I just tested it in Django 3.2 and everything works perfectly. – xyres Sep 13 '21 at 17:24
  • @xyres are you referring to the top answer with 67 upvotes? I cant figure out how to properly implement it – Wazy Sep 13 '21 at 22:13
  • @xyres see update – Wazy Sep 13 '21 at 22:32
  • In the `load` tag, you have to supply the name of the file containing the tag: `{% load file_name %}`. Yeah, it's not very intuitive. I've posted a detailed answer below. – xyres Sep 14 '21 at 04:57

2 Answers2

2

This can be done using custom template tags as shown in this answer. However, that answer skips over the whole process of creating custom tags, so let me show you how to do that.

Django requires that your custom tags be kept in a folder called templatetags inside an app. So this is what your app's folder structure must look like:

my-app/
├─ __ini__.py
├─ models.py
├─ views.py
├─ templatetags/
│  ├─ __init__.py
│  ├─ myapp_tags.py

Tip: You can also create a new app specifically for keeping custom tags. That way it becomes easy to re-use them in different projects. Make sure your new app is in INSTALLED_APPS list.

Now open the myapp_tags.py file and create the custom tag there:

# myapp_tags.py

from django import template

register = template.Library()

@register.simple_tag
def querystring_replace(request, key, value):
    """Replace the given `key` with the given `value`
    in the request's querystring. The rest of the
    querystring remains unchanged.
    """
    query_dict = request.GET.copy()
    query_dict[key] = value
    return query_dict.urlencode()

To use this tag in the templates, you'll need to load the file containing the tags:

 <!-- load the file containing the tags -->
{% load myapp_tags %}

<!-- now you can use all the tags from myapp_tags -->
{% querystring_replace request 'page' page_obj.next_page_number %}
xyres
  • 20,487
  • 3
  • 56
  • 85
  • got the tag working! Only issue is when i go to the next page it loads the proper url `manufacture=5&model=10&page=2` but it throws the error `Page not found` I think I just have to create a new url for it, will do more testing tonight – Wazy Sep 14 '21 at 16:48
  • 1
    @Wazy Are you using the question mark (`?`) before the querystring such as: `href="?{% ... %}"` – xyres Sep 14 '21 at 18:41
  • I did miss the question mark it is working now. Thank you so much for your help on this – Wazy Sep 14 '21 at 20:13
  • Got it mostly working now had to make a few changes, Only issue now is where I have the elif statment in the paginator if I set the href as `?{% querystring_replace request 'page' page_obj.next_page_number %}` if I am on page 4 and click on page 3 it would take me to page 5, going to try and figure out a workaround for this. However it does work fine with the forward and back btn as the urls are staying the same – Wazy Sep 15 '21 at 03:09
  • I setup two if statements that I posted on the question that work if the page is the the left or right of the current page but if you select a page that is not beside the current page it does not work – Wazy Sep 15 '21 at 03:27
0

Every web request is independent. the server doesn't remember what you've sent before. therefore client(browser) should keep(remember) current context and make up appropriate requests.

in following code, you didn't pass any filter parameters.

<a class="page-link" href="?page={{ page_obj.next_page_number }}"/>

above code actually rendered as follow

<a class="page-link" href="?page=3"/>

to paginate AND filter, you should pass filter args. the correct HTML should looks like below. there are more query string than before.

<a class="page-link" href="?page=3&FILTER_A=FOO&FILTER_B=BAR"/>

there are several ways to achieve appending extra query string based on former request's query string.

    class BaseFilteredTableView(SingleTableView):
        ...
        def get_queryset(self, **kwargs):
            qs = super().get_queryset(**kwargs)

            filter = BarFilter(self.request.GET, queryset=qs, **kwargs)
            return filter.qs
  • client side

    1. by JavaScript
//javascript
<script>
    console.log(window.location.href)
</script>

window.location.href will return query string. and you can parse it and append query string to each paginator a-link

kochul
  • 611
  • 6
  • 12
  • Thanks for the response, I understand that the issue is with the `page_obj.next_page_number` my issue is I cant find a way to bring the variable into the url – Wazy Sep 09 '21 at 16:24