implemented sorting and pagination on the product page, but sorting gets confused when moving to the next page. How do I apply a filter to all pagination pages?and i got smth weird with dropdown selection: after moving to next pages it starts act like crazy,after choosing a filter and refreshing the page, it returns to the default value and seems to be changing places (exmp: $$-$ -> $-$$ and vice versa , and i must guess who is who :) )
template
<div class="product-sorting d-flex">
<p>Sort by:</p>
<form name="selectForm" action="{% url 'shop' %}" method="get">
<label for="orderby"></label>
<select name="orderby" id="orderby" onchange="selectForm.submit();">
<option value="price">price: $$ - $</option>
<option value="-price">price: $ - $$</option>
</select>
<input type="submit" class="d-none" value="submit">
</form>
</div>
views
class Shop(ListView):
template_name = 'essense/shop.html'
context_object_name = 'items'
paginate_by = 9
allow_empty = False
model = Item
def get_context_data(self, *, object_list=None, **kwargs):
***context***
def get_ordering(self):
return self.request.GET.get('orderby', )
pagination
{% if page_obj.has_other_pages %}
</div>
<nav aria-label="navigation">
<ul class="pagination mt-50 mb-70">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link"
href="?page={{ page_obj.previous_page_number }}"><i
class="fa fa-angle-left"></i></a>
</li>
{% endif %}
{% for p in page_obj.paginator.page_range %}
{% if page_obj.number == p %}
<li class="page-item"><a class="page-link" href="#">{{ p }}</a></li>
{% elif p > page_obj.number|add:-3 and p < page_obj.number|add:+3 %}
<li class="page-item"><a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link"
href="?page={{ page_obj.next_page_number }}"><i
class="fa fa-angle-right"></i></a></li>
{% endif %}
</ul>
</nav>
</div>
</div>
{% endif %}
You are only passing the new page number with the pagination link, so any other parameters that may have been involved in filtering will get lost on the new page.
You need a way to maintain the extra GET params. One way to do this might be a template tag that you can pass the new page number to;
project/templatetags/pagination_tags.py:
from django import template
register = template.Library()
#register.simple_tag
def url_replace(request, field, value):
d = request.GET.copy()
d[field] = value
return d.urlencode()
Then in your template the next (and previous) links would use that tag:
{% load pagination_tags %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?{% url_replace request 'page' page_obj.next_page_number %}">
<i class="fa fa-angle-right"></i>
</a>
</li>
{% endif %}
Related
I am facing this probelm in my e-commerce project when i try to pagination
Cannot resolve keyword '' into field. Choices are: created_at, description, id, is_active, subcategories, thumbnail, title, url_slug
# CATEGORIES
class CategoriesListView(ListView):
model = Categories
template_name = "admin_local/category_list.html"
paginate_by = 3
def get_queryset(self):
filter_val = self.request.GET.get("filter", "")`enter code here`
order_by = self.request.GET.get("orderby", "id")
if filter_val != "":
cat = Categories.objects.filter(
Q(title__contains=filter_val) | Q(description__contains=filter_val)).order_by(order_by)
else:
cat = Categories.objects.all().order_by(order_by)
return cat
def get_context_data(self, **kwargs):
context = super(CategoriesListView, self).get_context_data(**kwargs)
context["filter"] = self.request.GET.get("filter", "")
context["orderby"] = self.request.GET.get("orderby", "")
context["all_table_fields"] = Categories._meta.get_fields()
return context
I am facing this probelm in my e-commerce project when i try to pagination
This Is html file
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link"
href="{% url 'category_list' %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.previous_page_number }} ">Previous</a>
</li>
{% else %}
<li class="page-item disabled"><a class="page-link"
href="#">Previous</a>
</li>
{% endif %}
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link"
href="{% url 'category_list' %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.next_page_number }} ">Next</a>
</li>
{% else %}
<li class="page-item disabled"><a class="page-link"
href="#">Previous</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
</div>
The reason you get this is because you use orderby={{ orderby }} which is empty if it is not filled in.
You should pass the context with:
context['orderby'] = self.request.GET.get('orderby', 'id')
furthermore you should |urlencode the parameters:
<a class="page-link" href="{% url 'category_list' %}?filter={{ filter|urlencode }}&orderby={{ orderby|urlencode }}&page={{ page_obj.next_page_number }} ">Next</a>
You might also want to avoid ordering by an arbitrary field, since ordering can expose sensitive information, for example trying to order by the (hashed) password of a user.
I am currently working on a django blog and I've coded a search bar where you type something and, if there's any post that contains what you've typed in the title, it should appear all the posts. This part is perfectly well-written. However, there's an error with the pagination. As you'll see in the views.py. The maximum num of posts per page is 3. However, you can see there's four. However, the paginator detects there should be another page for the fourth posts. Here's an image that shows that.
Here's the views.py:
class ElementSearchView(View):
elements_list = Element.objects.all()
def get(self, request, *args, **kwargs):
paginator = Paginator(self.elements_list, 3)
page_request_var = 'page'
page = request.GET.get(page_request_var)
try:
paginated_queryset = paginator.page(page)
except PageNotAnInteger:
paginated_queryset = paginator.page(1)
except EmptyPage:
paginated_queryset = paginator.page(paginator.num_pages)
queryset = Element.objects.all()
query = request.GET.get('q')
if query:
queryset = queryset.filter(
Q(title__icontains=query)
).distinct()
context = {
'query': queryset,
'queryset': paginated_queryset,
'page_request_var': page_request_var,
}
return render(request, 'periodic/search_results_table.html', context)
And here's the html template: The pagination is at the end of the template.
{% load static %}
<html>
{% include 'periodic/head_search.html' %}
<body>
{% include 'periodic/header.html' %}
<br>
<br>
<div class="container">
<div class="row">
<!-- Latest Posts -->
<main class="posts-listing col-lg-8">
<div class="container">
<div class="row">
<!-- post -->
{% for element in query %}
<div class="post col-xl-6 wrap-login100">
<div class="post-thumbnail"><img src="{{ element.thumbnail.url }}" alt="..." class="img-fluid"></div>
<div class="post-details">
<a href="{{ element.get_absolute_url }}">
<h3 class="h4">{{ element.title }}</h3></a>
</div>
</div>
{% endfor %}
</div>
<!-- Pagination -->
<nav aria-label="Page navigation example">
<ul class="pagination pagination-template d-flex justify-content-center">
{% if queryset.has_previous %}
<li class="page-item"> <i class="fa fa-angle-left"></i></li>
{% endif %}
<li class="page-item">{{ queryset.number }}</li>
{% if queryset.has_next %}
<li class="page-item"> <i class="fa fa-angle-right"></i></li>
{% endif %}
</ul>
</nav>
{% if is_paginated %}
<nav aria-label="Page navigation example">
<ul class="pagination pagination-template d-flex justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item"> <i class="fa fa-angle-left"></i></li>
{% endif %}
<li class="page-item">{{ page_obj.number }}</li>
{% if page_obj.has_next %}
<li class="page-item"> <i class="fa fa-angle-right"></i></li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</main>
</body>
{% include 'periodic/scripts_search.html' %}
</html>
As the user #ikilnac mentioned,
you are looping over the original queryset and not the paged one in your template
That simply means that in your loop, you do this
{% for element in query %}
<div class="post col-xl-6 wrap-login100">
<div class="post-thumbnail"><img src="{{ element.thumbnail.url }}" alt="..." class="img-fluid"></div>
<div class="post-details">
<a href="{{ element.get_absolute_url }}">
<h3 class="h4">{{ element.title }}</h3></a>
</div>
</div>
{% endfor %}
And then after that, you are using the tag in the form of {% if queryset.has_previous %} with the original queryset rather than the paged one in the template (the query).
There's a good example of it here - Paginate with Django
I'm trying to add the paginate option on my posts page from my blog website. I want to implement the paginator on my view function that renders the page and I found this exemple from Django's doc but it doesn't work. Any help ?
view function:
def blog(request):
posts = Post.objects.all().order_by('-date_posted')
paginator = Paginator(posts, 2)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'posts': posts,
'page_obj': page_obj,
'title': 'Blog',
'banner_page_title': 'Blog',
'page_location': 'Home / Blog'
}
return render(request, 'blog/blog.html', context)
html rendering
<nav class="blog-pagination justify-content-center d-flex">
<ul class="pagination">
{% if is_paginated %}
{% if page_obj.has_previous %}
<li class="page-item">
<a href="?page={{ page_obj.previous_page_number }}" class="page-link" aria-label="Previous">
<span aria-hidden="true">
<span class="ti-arrow-left"></span>
</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
{{ num }}
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
{{ num }}
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a href="?page={{ page_obj.next_page_number }}" class="page-link" aria-label="Next">
<span aria-hidden="true">
<span class="ti-arrow-right"></span>
</span>
</a>
</li>
{% endif %}
{% endif %}
</ul>
</nav>
the url route
path('blog/', blog, name='blog'),
Remove the {% if is_paginated %} as it isn't returning any value and that is why you don't see any of the numbers (don't forget to remove the closer if {% endif %})
You can access your paginated objects from the page_obj
{% for post in page_obj %}
{{ post }}
{% endfor %}
Here is the HTML with the changes
{% for post in page_obj %}
{{ post.text }}<br>
{% endfor %}
<nav class="blog-pagination justify-content-center d-flex">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a href="?page={{ page_obj.previous_page_number }}" class="page-link" aria-label="Previous">
<span aria-hidden="true">
<span class="ti-arrow-left"></span>
</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
{{ num }}
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
{{ num }}
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a href="?page={{ page_obj.next_page_number }}" class="page-link" aria-label="Next">
<span aria-hidden="true">
<span class="ti-arrow-right"></span>
</span>
</a>
</li>
{% endif %}
</ul>
</nav>
I have a template which includes 4 different paginators that only differ in context variables (tasks_today, tasks_tomorrow, ... etc.), and I want to minimise code repetition so I don't have 4 different paginator templates.
Template:
<div class="wrapper">
<h3>Today</h3>
<table>
{% if tasks_today %}
{% for task in tasks_today %}
{% include 'todo/task_table_row.html' %}
{% endfor %}
{% include 'todo/paginator_today.html' %}
{% else %}
<p>No tasks for today.</p>
{% endif %}
</table>
<h3>Tomorrow</h3>
<table>
{% if tasks_tomorrow %}
{% for task in tasks_tomorrow %}
{% include 'todo/task_table_row.html' %}
{% endfor %}
{% include 'todo/paginator_tomorrow.html' %}
{% else %}
<p>No tasks for tomorrow.</p>
{% endif %}
</table>
<h3>Upcoming</h3>
<table>
{% if tasks_upcoming %}
{% for task in tasks_upcoming %}
{% include 'todo/task_table_row.html' %}
{% endfor %}
{% include 'todo/paginator_upcoming.html' %}
{% else %}
<p>No upcoming tasks.</p>
{% endif %}
</table>
<h3>Past</h3>
<table>
{% if tasks_past %}
{% for task in tasks_past %}
{% include 'todo/task_table_row.html' %}
{% endfor %}
{% include 'todo/paginator_past.html' %}
{% else %}
<p>No tasks in the past.</p>
{% endif %}
</table>
</div>
paginator_today:
{% load url_replace %}
{% if tasks_today %}
<div class='paginator'>
<nav aria-label="Page navigation">
<ul class="pagination">
{% if tasks_today.has_previous %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_today=1 %}" aria-label="Previous">
<span aria-hidden="true">«</span>
<span class="sr-only">begin</span>
</a>
</li>
{% endif %}
{% for n in tasks_today.paginator.page_range %}
{% if tasks_today.number == n %}
<li class="page-item active">
<span class="page-link">{{ n }}
<span class="sr-only">(current)</span>
</span>
</li>
{% elif n > tasks_today.number|add:'-3' and n < tasks_today.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_today=n %}">{{ n }}</a>
</li>
{% endif %}
{% endfor %}
{% if tasks_today.has_next %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_today=tasks_today.paginator.num_pages %}" aria-label="Next">
<span aria-hidden="true">»</span>
<span class="sr-only">end</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
paginator_tomorrow:
{% load url_replace %}
{% if tasks_tomorrow %}
<div class='paginator'>
<nav aria-label="Page navigation">
<ul class="pagination">
{% if tasks_tomorrow.has_previous %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_tomorrow=1 %}" aria-label="Previous">
<span aria-hidden="true">«</span>
<span class="sr-only">begin</span>
</a>
</li>
{% endif %}
{% for n in tasks_tomorrow.paginator.page_range %}
{% if tasks_tomorrow.number == n %}
<li class="page-item active">
<span class="page-link">{{ n }}
<span class="sr-only">(current)</span>
</span>
</li>
{% elif n > tasks_tomorrow.number|add:'-3' and n < tasks_tomorrow.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_tomorrow=n %}">{{ n }}</a>
</li>
{% endif %}
{% endfor %}
{% if tasks_tomorrow.has_next %}
<li class="page-item">
<a class="page-link" href="?{% url_replace page_tomorrow=tasks_tomorrow.paginator.num_pages %}" aria-label="Next">
<span aria-hidden="true">»</span>
<span class="sr-only">end</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
What can I do?
url_replace.py:
from django.utils.http import urlencode
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def url_replace(context, **kwargs):
query = context['request'].GET.dict()
query.update(kwargs)
return urlencode(query)
You can use the with keyword to set certain variables inside your include template:
{% include 'todo/paginator.html' with tasks=tasks_today page='page_today' %}
You can then replace references to tasks_today in you paginator template to tasks, as well as the reference to page_today to page
Edit: The call to url_replace is problematic, since it uses kwargs, and a call like {% url_replace page_today = x %}, will be equivalent to a call to urlreplace(context, **{'page_today' : x}).
Simply calling url_replace with page = x will not work correctly, since it will pass a keyword argument with the name of page instead of the value of page
The naive solution would be to pass a page variable to the paginator.html and call {% urlreplace **{page : 20} %}, but the Django template engine does not allow packing kwargs.
I think the best way to go here is to create a url_replace_single function, since in this use case you only replaces one value.
To do this, you'll need to define a field and value parameter on you url_replace function:
#register.simple_tag(takes_context=True)
def url_replace(context, field, value, **kwargs):
query = context['request'].GET.dict()
query_dict = { field : value}
query.update(query_dict)
return urlencode(query)
Then call the function from your template like this:
{% url_replace field=page value=x %}
The only downside is that this will only let you replace one query parameter in your url.
Code:
class MyObjects(LoginRequiredMixin, ListView):
model = AllObjects
template_name = "my_objects.html"
paginate_by = 10
def get_queryset(self, *args, **kwargs):
queryset = super(MyObjects, self).get_queryset()
return queryset.filter(added_by=self.request.user).order_by('-last_modified')
I have a view that lists user's addings to a table. The problem is pagination is not working properly. I think it is because each time django renders the page, it filters the queryset again, causing only first 10 items to be listed. What can be done to handle this problem? Or should i not use generic view for doing such thing?
Template:
<table>
{% for obj in object_list %}
<tr>
<td>
{{ obj.name}}
</td>
</tr>
{% endfor %}
</table>
<nav aria-label="Page navigation">
<ul class="pagination">
<li class="page-item {% if not page_obj.has_previous %}disabled{%endif%}">
<a class="page-link" {% if page_obj.has_previous %} href="?sayfa={{ page_obj.previous_page_number }}" {% endif %} aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for page in page_obj.paginator.page_range %} {% if page == page_obj.number %}
<li class="page-item active"><a class="page-link" href="#">{{ page }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?sayfa={{ page }}">{{ page }}</a></li>
{% endif %} {% endfor %}
<li class="page-item {% if not page_obj.has_next %}disabled{%endif%}">
<a class="page-link" {% if page_obj.has_next %}href="?sayfa={{ page_obj.next_page_number }}" {% endif %} aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
I found my mistake. In my template, i used the link "?sayfa={{ page }}". ("sayfa" means page in Turkish, so it's localized version) It's a leftover from my previous pagination which i made with function based view. One way to fix is changing all "sayfa"'s to "page" but since this is not great for localization purposes adding page_kwarg = "sayfa" to my view fixed the problem. Thanks for the answers!