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 %}
Related
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.
I am new to Django and trying to refactor my function-based views to CBVs.
I got everything else working, and the pagination works(it shows "JOBS_PER_PAGE" jobs on each page), but the only problem is that in the template, page_obj does not work. Although the page has next pages, the page navigator is all greyed out and disabled. page_obj.number doesn't return anything. I looked at other posts, but I couldn't find any useful information.
Is there any extra step I need to do to get page_obj to work correctly in the template?
urls.py
urlpatterns = [
path('', views.landing_page, name='landing-page'),
path('search/<int:pk>/', views.SearchView.as_view(), name='search'),
path('apply/<int:pk>/', views.apply, name='apply'),
path('result/<int:pk>/', views.result, name='result'),
path('start/', views.start, name='start'),
path('eor/<int:pk>', views.end_of_round, name='end-of-round'),
path('closing/<int:pk>', views.closing, name='closing')
]
views.py
class SearchView(ListView):
model = Job
context_object_name = 'jobs'
template_name = 'simulation/search.html'
paginate_by = JOBS_PER_PAGE
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.respondent = Respondent.objects.get(pk=self.kwargs['pk'])
if self.respondent.is_out_of_money():
self.respondent.increment_round()
self.respondent.save()
return redirect('simulation:end-of-round', pk=self.respondent.id)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['respondent'] = self.respondent
return context
def post(self, request, *args, **kwargs):
self.respondent.subtract_from_balance(request.POST.get('more_info_fee'))
self.respondent.add_to_jobs_more_info(request.POST.get('selected_job'))
self.respondent.log_time_spent()
self.respondent.increment_stage()
self.respondent.save()
return redirect('simulation:search', pk=self.respondent.id)
search.html
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">{{ page_obj.previous_page_number }}</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true">Previous</a>
</li>
{% endif %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.number }}">{{ page_obj.number }}</a>
</li>
{% if jobs.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">{{ page_obj.next_page_number }}</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true">Next</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
EDIT:
The template search.html is structured as
{% block A %}
{% endblock A %}
CODE ABOVE
{% block B %}
{% endblock B %}
The page_obj is accessible in block A and block B, but not accessible in between blocks.
[1]: https://i.stack.imgur.com/jr7Dx.png
The problem was my template was nested and the pagination code was duplicated in another template that search.html inherits from.
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.
Sorry because of the dumb question. I am reading a tutorial from book called build django 2 web application. and I get to the pagination topic but I can't get why it does not working even when I am copy-pasting carefully.
{% if is_paginated %}
<nav>
<ul class="pagination">
<li class="page-item">
First
</li>
{% if page_obj.has_previous %}
<li class="page-item">
{{page_obj.previous_page_number}}
</li>
{% endif %}
<li class="page-item active">
{{page_obj.number}}
</li>
{% if page_obj.has_next %}
<li class="page-item">
{{page_obj.next_page_number}}
</li>
{% endif %}
<li class="page-item">
Last
</li>
</ul>
</nav>
{% endif %}
#View
class MovieListView(ListView):
model = Movie
template_name = 'movie_list.html'
You haven't set the paginated_by attribute in the view class, so the contents won't be paginated.
class MovieListView(ListView):
model = Movie
template_name = 'movie_list.html'
paginate_by = 5
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>