There is new option to create pagination range - get_elided_page_range
https://docs.djangoproject.com/en/3.2/ref/paginator/#django.core.paginator.Paginator.get_elided_page_range
How should I use it? How can I set args? I am using CBV ListView.
I tried https://nemecek.be/blog/105/how-to-use-elided-pagination-in-django-and-solve-too-many-pages-problem but it didn't work for me.
I have 81 pages and current page is 10. Problem is I am always have range 1 2 3 4 ... 80 81
What am I doing wrong?
#views.py
class TrailersListView(ListView):
queryset = Trailer.objects.all()
paginate_by = 10
#template.html
{% for i in paginator.get_elided_page_range %}
{% if page_obj.number == i %}
<li class="active page-item">
<span class="page-link">{{ i }}</span>
</li>
{% else %}
{% if i == paginator.ELLIPSIS %}
<li class="page-item">
<span class="page-link">{{ paginator.ELLIPSIS }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
The page number is not passed to the "get_elided_page_range" method and it uses the default value (1) all the time. So I created a custom tag to pass it a number.
appname/templatetags/paginator_tags.py
from django import template
from django.core.paginator import Paginator
register = template.Library()
#register.simple_tag
def get_proper_elided_page_range(p, number, on_each_side=3, on_ends=2):
paginator = Paginator(p.object_list, p.per_page)
return paginator.get_elided_page_range(number=number,
on_each_side=on_each_side,
on_ends=on_ends)
template.html
{% load paginator_tags %}
{% get_proper_elided_page_range paginator page_obj.number as page_range %} <!-- here -->
{% for page_num in page_range %}
{% if page_obj.number == i %}
<li class="active page-item">
<span class="page-link">{{ i }}</span>
</li>
{% else %}
{% if i == paginator.ELLIPSIS %}
<li class="page-item">
<span class="page-link">{{ paginator.ELLIPSIS }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
You can add additional arguments, e.g:
{% get_proper_elided_page_range paginator page_obj.number 1 1 as page_range %}
If you are using a class-based view such as ListView in the question's example, then if you trigger pagination by setting the paginate_by member, the page_obj object is available in the get_context_data method. From this method, you can invoke get_elided_page_range on the Paginator to set extra context for the template being rendered.
Example:
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
page: Page = context['page_obj']
context['paginator_range'] = page.paginator.get_elided_page_range(page.number)
return context
You can then access paginator_range from within your template.
Usage note: this method returns a generator which yields either an int (which is a valid page number) or a str (…), which is not.
Related
I use Paginator for paginate my posts, i have no error, and i have the list of page in the bottom of the posts, but:
1.Post doesn't paginate correctly, i have set 5 i have more.
2. When i click on the 2 second page and 3 and etc, i have the same results of posts, i have no the next page with nexts posts.
This is my view code:
def post_all(request):
posts = Post.objects.filter().order_by('-published_date')
paginator = Paginator(posts, 5)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
categories = PostCategory.objects.all().annotate(posts_count=Count('post'))
return render(request, 'front/blog/post_all.html', {"posts":posts, "categories":categories,"page_obj":page_obj})
My template:
<!-- Pagination -->
<div class="pagination">
{% if page_obj.has_previous %}
<i class="fa fa-angle-left"></i>
{% endif %}
{{ page_obj.number }} de {{ page_obj.paginator.num_pages }}
{% if page_obj.has_next %}
<i class="fa fa-angle-right"></i>
{% endif %}
</div>
<!-- End Pagination -->
Thank u.
You could try using Class-based views.
Yours would be something like this :
views.py
from django.views.generic import (ListView)
class PostAllView(ListView):
model = Post
template_name = 'front/blog/post_all.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-published_date']
paginate_by = 5
and a template.html that you can adapt to yours:
<h1>Total of posts : {{ page_obj.paginator.count }}</h1>
{% for post in posts %}
<article>
<p class="article-content">{{ post.content }}</p>
<!-- all the post related content you want to display here-->
</article>
{% endfor %}
{% if is_paginated %}
{% if page_obj.has_previous %}
<!-- you can adapt the class to your use-->
<a class="button" href="?page=1">First</a>
<a class="button" 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="button-strong" href="?page={{ num }}">{{ num }}</a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="button" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="button" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class="button" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
{% endif %}
i'm trying to use Django Paginator using ListView, but the paginator shows post title instead of page number:
Page number replaced with post title
how can i fix that?
this is my code:
in views.py:
from django.shortcuts import render
from django.core.paginator import Paginator
from django.views.generic import ListView, DetailView
from .models import Post
# Create your views here.
# PostObject_list by using ListView
class PostList(ListView):
model=Post
paginate_by=20
context_object_name='all_posts'
ordering=['-created_at']
# allpost by using ListView
'''class AllPost(ListView):
model=Post
context_object_name='allpost'
paginate_by=None
'''
# Post_singl_object using DetailView
class PostDetail(DetailView):
model=Post
in template post_list.html :
</div>
<nav aria-label="Page navigation example">
<ul class="pagination d-flex justify-content-center mt-1">
{% if all_posts.has_previous %}
<li class="page-item "><a class="page-link" href="?page={{ all_posts.previous_page_number }}">Previous</a></li>
{% endif %}
{% for page in all_posts %}
<li class="page-item ">
<a class="page-link" href="?page={{ page }}"><span>{{page}}</span></a>
</li>
{% endfor %}
{% if all_posts.has_next %}
<li class="page-item"><a class="page-link" href="?page={{all_posts.previous_page_number }}">Next</a></li>
{% endif %}
</ul>
</nav>
</div>
<!-- Pagination-->
thank you!
Using all_posts will not work because this is the object list, not the page object. all_posts will not have the page attributes like paginator, has_previous, previous_page_number, etc.
In this case you need to use page_obj, which is automatically added by ListView to the template context when paginating. You can get the page range from the template using page_obj.paginator.page_range:
{% for page in page_obj.paginator.page_range %}
<li class="page-item ">
<a class="page-link" href="?page={{ page }}"><span>{{ page }}</span></a>
</li>
{% endfor %}
The implementation for Previous and Next will also not work because of the same reason. To resolve that, you also need to use page_obj, so all in all:
{% if page_obj.has_previous %}
<li class="page-item "><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a></li>
{% endif %}
{% for page in page_obj.paginator.page_range %}
<li class="page-item ">
<a class="page-link" href="?page={{ page }}"><span>{{ page }}</span></a>
</li>
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li>
{% endif %}
You should access to paginator. In your case, page_obj.paginator. Try to change your template to:
{% for i in page_obj.paginator.page_range %}
<li class="page-item ">
<a class="page-link" href="?page={{ page }}"><span>{{page}}</span></a>
</li>
{% endfor %}
I implemented the Django pagination to slice the results of the search on my site.
# views.py
def search(request):
# Keywords
if 'keywords' in request.GET:
keywords = request.GET['keywords']
if keywords:
title_list_control_valve = queryset_list.filter(title__icontains=keywords).order_by('title')
description_list_control_valve = queryset_list.filter(description__icontains=keywords).order_by('description').distinct('description')
result_list_control_valves = list(chain(title_list_control_valve,
description_list_control_valve))
result_list_control_valves_2 = list(dict.fromkeys(result_list_control_valves))
paginator = Paginator(result_list_control_valves_2, 1)
page = request.GET.get('page')
paged_queries = paginator.get_page(page)
context = {
'queries': paged_queries,
}
return render(request, 'pages/search.html', context)
else:
return render(request, 'pages/search.html')
else:
return render(request, 'pages/search.html')
the URL of the site when showing search results is :
http://localhost:8000/pages/search/?keywords=something
It works perfectly fine on the first page only. From the second page on, it shows an empty page, and the URL of the site changes to:
http://localhost:8000/pages/search/?page=2
I link to other pages in the HTML as following:
<div class="col-md-12">
{% if queries.has_other_pages %}
<ul class="pagination">
{% if queries.has_previous %}
<li class="page-item">
«
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link">«</a>
</li>
{% endif %}
{% for i in queries.paginator.page_range %}
{% if queries.number == i %}
<li class="page-item active">
<a class="page-link">{{i}}</a>
</li>
{% else %}
<li class="page-item">
{{i}}
</li>
{% endif %}
{% endfor %}
{% if queries.has_next %}
<li class="page-item">
»
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link">»</a>
</li>
{% endif %}
</ul>
{% endif %}
</div>
The ?keywords=something part of the URL goes away.
is it the problem?
how to make the other page's link work too?
Add keywords to your GET params. For ex. href="?page={{queries.previous_page_number}}&keywords={{request.GET.keywords}}"
in django, i am trying to list some queries of several objects like user lists, categoies and Post list. the homepage will be contained couple of blocks or boxes. each box will have different query list like Post list, User List, category list. But only one context will have pagination and other won't and ofcourse the pagination will be working on Post list.
here is the views.py:
class BlogappListView(ListView):
model = Category,CustomUser
template_name = 'home.html'
context_object_name='category_list'
queryset=Category.objects.all()
paginate_by = 2
def get_context_data(self, **kwargs):
context = super(BlogappListView, self).get_context_data(**kwargs)
context['user_list']= CustomUser.objects.all()
context['post_list']=Post.objects.all()
activities=context['post_list']
return context
def get_related_activities(self,activities):
queryset=self.objects.activity_rel.all()
paginator=Paginator(queryset,2)
page=self.request.GET.get('page')
activities=paginator.get_page(page)
return(activities)
in urls.py:
urlpatterns = [
path('',BlogappListView.as_view(),name='home'),
]
in base.html, the paginate portion code:
<ul class="pagination justify-content-center mb-4">
{% if is_paginated %}
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
{% endif %}
</ul>
the main problem is that page number 1 is showing all the posts and also the second page. and category list is also truncated into 2 but the user list ok.
so how can i make it work ? is there any other way?
thank you in advance
i solved it. the revised code is:
class BlogappListView(ListView):
model = Category,CustomUser
template_name = 'home.html'
context_object_name='post_list'
queryset=Post.objects.all()
paginate_by=2
def get_context_data(self, **kwargs):
context = super(BlogappListView, self).get_context_data(**kwargs)
context['user_list']= CustomUser.objects.all()
context['category_list']=Category.objects.all()
return context
while looking for solution, i've got somewhere written that pagination will work only with queryset, not with the context. and that was so straight cut to understand and when i use paginate_by=2 in the queryset under Post it will only work for that.
the solution was quick and short but it took me a while to figure it out which is not good. but either way thanx to this platform to have this discussion and i am keeping this solution so that someone might get help from it.
I have a class based view that I am using to display a queryset in a table. I am also using a couple formsets to filter this queryset. I am using the get_queryset() method provided as part of the generic.ListView class to filter the diplayed results. Here is basically what my class looks like:
from django.views import generic
class UnifiedSingleSearch(generic.ListView):
template_name = 'app/foo.html'
model = MyModel
paginate_by = 30
def get_queryset(self):
if self.request.POST: # If we got here because of a search submission, filter
return MyModel.objects.filter('Some stuff base on the POST data')
return MyModel.objects.all() # Otherwise, just show everything
Because I am using a formset to submit multiple search criteria, I have to use a POST request. Upon initial submission of the form, the page reloads with a correctly filtered querset. However when I try to use my pagination controls, the POST request is thrown away and the page acts as if I am going to page#2 of MyModel.objects.all() instead of my filtered down subset.
How can I retain my filtered queryset when using pagination controls?
Here is the HTML for the pagination controls:
{% if is_paginated %}
<nav aria-label="Pagination nav">
<ul class="pagination">
{# Back a page #}
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">❮</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">❮</span>
</li>
{% endif %}
{# Page numbers #}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<span class="page-link">{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endfor %}
{# Next page #}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">❯</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">❯</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<p>No MyModel objects found</p>
{% endif %}
Switching to a GET request was the answer. Pagination controls also had to be edited a bit to retain the passed querystring. They should all include {{ request.GET.urlencode }} and then you tack on the page logic to the end like usual. Ends up looking something like this:
<li class="page-item">
<a class="page-link" href="?{{ request.GET.urlencode }}&page={{ page_obj.next_page_number }}">❯</a>
</li>