Reputation: 494
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">«</span>
</a>
{% else %}
</li>
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</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">»</span>
</a>
{% else %}
</li>
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">»</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 %}
Upvotes: 6
Views: 661
Reputation: 21844
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 %}
Upvotes: 2
Reputation: 681
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.
server side
there are already same Q&A. and there are many answers by 1&2 ways.
How to paginate Django with other get variables?
https://django-tables2.readthedocs.io/en/latest/#
Django-tables is a good option to render a table. this package has its own paginator template(HTML) and template tags and other codes. if you choose to use this, below code might help to use both filter and table which has built-in paginator.
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
//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
Upvotes: 0