Django pagination - How to limit the pages? - django

I'm using this code to display the pagination on my page
<ul class="pagination pagination-sm">
{% if restaurants.has_previous %}
<li>Prev</li>
{% endif %}
{% for page in restaurants.paginator.page_range %}
<li class="{% if restaurants.number == page %}active{% endif %}">{{ page }}</li>
{% endfor %}
{% if restaurants.has_next %}
<li>Next</li>
{% endif %}
</ul>
The issue is that the code above shows all page like this:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | and so on..
Instead I would like to show something like this:
1 | 2 | 3 | 4 | 5
How can I do that?
This is my view:
def listing(request):
list_restaurants = ShopAccount.objects.filter(is_active=1)
# only active products
paginator = Paginator(list_restaurants, 20) # Show 20 products per page
page = request.GET.get('page')
try:
restaurants = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
restaurants = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
restaurants = paginator.page(paginator.num_pages)
return render_to_response('restaurants/list.html', {'restaurants': restaurants}, context_instance=RequestContext(request))

I use something like this :
<li {% ifequal page current %}class="active"{% endifequal %}>
<a href={% ifequal page -4 %}
"/blog/1/"><<
{% else %}{% ifequal page -3 %}
"/blog/{{ current | add:"-1" }}/"><
{% else %}{% ifequal page -2 %}
"/blog/{{ current | add:"1" }}/">>
{% else %}{% ifequal page -1 %}
"/blog/{{ maximum }}/">>>
{% else %}{% ifequal page 0 %}
"#">...
{% else %}{% ifequal page current %}
"#" class="page_link">{{ page }}
{% else %}
"/blog/{{ page }}/">{{ page }}
{% endifequal %}
{% endifequal %}
{% endifequal %}
{% endifequal %}
{% endifequal %}
{% endifequal %}
</a>
</li>
and these views :
def makepaginator(current, maximum):
"""makepaginator() returns a set of number which reprensents buttons.
-4 means first page.
-3 means previous page.
-2 means next page.
-1 means last page.
0 means a dot.
others means the number of the target page."""
NB_PAGES_LEFT_LEFT = 3
NB_PAGES_RIGHT_RIGHT = NB_PAGES_LEFT_LEFT
NB_PAGES_CENTER_RIGHT = 3
NB_PAGES_CENTER_LEFT = NB_PAGES_CENTER_RIGHT
i = 2
current = int(current)
if current > 1:
p = [-4, -3]
else:
p = []
p.append(1)
while i <= maximum:
if (i > NB_PAGES_LEFT_LEFT and i < maximum - NB_PAGES_RIGHT_RIGHT
and (i - current > NB_PAGES_CENTER_RIGHT
or current - i > NB_PAGES_CENTER_RIGHT + 1)):
p.append(0)
while (i > NB_PAGES_LEFT_LEFT
and i < maximum - NB_PAGES_RIGHT_RIGHT
and (i - current > NB_PAGES_CENTER_RIGHT
or current - i > NB_PAGES_CENTER_LEFT + 1)):
i += 1
else:
p.append(i)
i += 1
if current < maximum:
p.append(-2)
p.append(-1)
return (p)
def news(request, page_num=1):
paginator = Paginator(Article.objects.all().order_by('-date'), settings.ARTICLES_PER_PAGE)
try :
page = paginator.page(page_num)
print(type(page_num))
return (render(request, "main/blog.html",
{"paginator" : paginator, "page_num" : int(page_num),
"page" : page,
"buttons" : makepaginator(page_num,
paginator.num_pages)}))
except (PageNotAnInteger, EmptyPage) as e:
return (notfound(request))
With bootstrap I get this :
It may look dirty but it works with my website. You can try it.
If you have some improvements to suggests, I would be glad though. :)

ShopAccount.objects.filter(is_active=1)[:5]
Try this code above, or use the [:5] on the paginator.

I solved this issue with JS.
Here the views:
class AllOrders(LoginRequiredMixin, ListView):
model = Order
template_name = 'all_orders.html'
paginate_by = 20
def get_queryset(self):
filter_val = self.request.GET.get("filter", "")
order_by = self.request.GET.get("orderby", "id")
if filter_val != "":
orders = Order.objects.filter(Q(transaction_id__contains=filter_val) | Q(customer_id__auth_user_id__first_name__contains=filter_val) |
Q(customer_id__auth_user_id__last_name__contains=filter_val)).order_by(order_by)
else:
orders = Order.objects.all().order_by(order_by)
return orders
def get_context_data(self, **kwargs):
context = super(AllOrders, self).get_context_data(**kwargs)
context["filter"] = self.request.GET.get("filter", "")
context["orderby"] = self.request.GET.get("orderby", "id")
context["all_table_fields"] = Order._meta.get_fields()
return context
Here the template:
<nav class="d-inline-block">
<ul class="pagination mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="{% url 'custom_user:all_orders' %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.previous_page_number }}" tabindex="-1"><i class="fas fa-chevron-left"></i></a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1"><i class="fas fa-chevron-left"></i></a>
</li>
{% endif %}
{% for i in paginator.page_range %}
<li class="page-item {% if i == page_obj.number %}active{% endif %}"><a class="page-link" href="{% url 'custom_user:all_orders' %}?filter={{ filter }}&orderby={{ orderby }}&page={{ i }}">{{ i }} <span class="sr-only">(current)</span></a></li>
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="{% url 'custom_user:all_orders' %}?filter={{ filter }}&orderby={{ orderby }}&page={{ page_obj.next_page_number }}"><i class="fas fa-chevron-right"></i></a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#"><i class="fas fa-chevron-right"></i></a>
</li>
{% endif %}
</ul>
</nav>
Here the JS:
<script>
let pages = document.getElementsByClassName('page-item');
let pageActive;
if (pages.length > 5) {
for (let i = 0; i < pages.length; i++) {
if (pages[i].classList.contains('active')){
pageActive = i;
break;
}
}
for (let j = 0; j < pages.length; j++) {
if ( j != 0 && j != 1 && j < (pageActive - 1)) {
pages[j].setAttribute('hidden', true);
} else if (j != (pages.length - 1) && j != (pages.length - 2) && j > (pageActive + 1)) {
pages[j].setAttribute('hidden', true);
}
};
};
</script>
This is the result:
paginator

Related

Paginator not working properly while filtering the querysets?

Here filtering working fine and also paginator works fine on the first page. The issue is while going on the next page if it has any.It shows the remaining data on the next page but if I go on the next page then It displays 0 data.
Paginator doesnot working in the filter view only. So I think the issue is in this view only since I have the same template for search and list queryset view and in there the pagination works fine.
EDIT: I am using the same template with same context name for search, filter and list view but only in filter the pagination issue came.
search url looks like this
(at first) search/?q= and in the next page search/?page=2 while going previous page search/?page=1 # this works fine
While doing filter I expect the same but it is not working. In the filter/?page=2 there is no data.
views
def get(self, request):
results = MyModel.objects.none()
parameter = request.GET.get('param', '')
today = datetime.datetime.today()
current_month = today.month
past_7_days = today - datetime.timedelta(days=7)
if parameter == '0':
return redirect('show_all_querysets')
elif parameter == '1':
results = MyModel.objects.filter(datetime__date=today.date()).order_by('-datetime')
elif parameter == '2':
results = MyModel.objects.filter(datetime__range=[past_7_days, today]).order_by('-datetime')
elif parameter == '3':
results = MyModel.objects.filter(datetime__month=current_month).order_by('-datetime')
querysets = Paginator(results, 10).get_page(request.GET.get('page'))
return render(request, 'show_all_querysets.html', {'querysets': querysets})
Search View
def get(self, request):
q = request.GET.get('q', '')
results = MyModel.objects.filter(message__icontains=q)).order_by('-datetime')
querysets = Paginator(results, 10).get_page(request.GET.get('page'))
return render(request, 'show_all_querysets.html', {'querysets': querysets})
template
<div class="paginate-navigate ml-3">
{% if querysets.has_previous %}
<a href="?{% if prev_url %}{{ prev_url }}{% endif %}page={{ querysets.previous_page_number}}">
<i title="previous" class="ic-chevron-left left"></i>
</a>
{% else %}
<a disabled href="#">
<i class="ic-chevron-left left"></i>
</a>
{% endif %}
{% if querysets.has_next %}
<a href="?{% if prev_url %}{{ prev_url }}{% endif %}page={{ querysets.next_page_number}}">
<i title="next" class="ic-chevron-right right ml-2"></i>
</a>
{% else %}
<a href="#">
<i class="ic-chevron-right right ml-2"></i>
</a>
{% endif %}
</div>
If you have both search fileter and pagination in query parameters then your url goes like your_url/?params=some&page=2
to achieve this first pass your params in context
return render(request, 'show_all_querysets.html', {'querysets': querysets,'parameter':parameter})
and then suppose you have previous and next buttons, so give href like this:
{% if querysets.has_previous %}
<a class="btn btn-outline-dark" href="{% if parameter %}{{ request.get_full_path }}&page={{ querysets.previous_page_number }}{% else %}?page={{ querysets.previous_page_number }}{% endif %}">Previous</a>
{% else %}
<button type="button" disabled class="btn btn-outline-dark" title = "No querysets available">Previous</button>
{% endif %}
{% if querysets.has_next %}
<a class="btn btn-outline-dark" href="{% if parameter %}{{ request.get_full_path }}&page={{ querysets.next_page_number }}{% else %}?page={{ querysets.next_page_number }}{% endif %}">Next</a>
{% else %}
<button disabled type="button" class="btn btn-outline-dark" title = "No querysets available">Next</button>
{% endif %}
querysets = Paginator(results, 10).page(request.GET.get('page'))

Django pagination - 5k objects

As a continuation of this question Django pagination with bootstrap any my works under my Contacts app I have to ask for your help once more. I have a problem I cannot resolve. I used few suggestions about pagination but all that is changing is the bottom pagination menu. My app is still loading all 5k objects every time, over and over. I am new at this, this app is my first project and this is the final 'big' missing part. I promiss to post final cone when I'm done :)
Best regards.
views.py
def index(request):
contacts_list = contacts.objects.all()
contacts_filter = LFilter(request.GET, queryset=contacts_list)
paginator = Paginator(contacts_list, 50)
try:
page = int(request.GET.get('page','1'))
except:
page = 1
try:
contacts = paginator.page(page)
except PageNotAnInteger:
contacts = paginator.page(1)
except EmptyPage:
contacts = paginator.page(paginator.num_pages)
index = contacts.number - 1
max_index = len(paginator.page_range)
start_index = index - 3 if index >= 3 else 0
end_index = index + 3 if index <= max_index - 3 else max_index
page_range = paginator.page_range[start_index:end_index]
return render(
request, 'index.html',
context={'filter': contacts_filter,
'contacts': contacts,
'page_range': page_range, }
)`
index.py
{% for obj in filter.qs %}
Code for contact information to display
{% endfor %}
<div class="prev_next">
{% if contacts.has_previous %}
<a class="prev btn btn-info" href="?page={{contacts.previous_page_number}}">Prev</a>
{% endif %}
{% if contacts.has_next %}
<a class="next btn btn-info" href="?page={{contacts.next_page_number}}">Next</a>
{% endif %}
<div class="pages">
<ul>
{% for pg in page_range %}
{% if contacts.number == pg %}
<li>{{pg}}</li>
{% else %}
<li>{{pg}}</li>
{% endif %}
{% endfor %}
</ul>
</div>
<span class="clear_both"></span>
</div>

What's the Django way of doing an "order by" dropdown filter?

I need a basic search query 'order by' dropdown to use with a RawQuerySet. I did some searching and couldn't find a specific example although the existence of Forms and Widgets which i don't fully understand makes me think I’m reinventing the wheel.
view
def search(request, limit=1000):
query = request.GET.get('q')
mapping = OrderedDict([
('time_desc', 'time_modified DESC'),
('time_asc', 'time_modified ASC'),
('relevance', None)])
sort = request.GET.get('sort')
_ = mapping.get(sort)
order_by = 'ORDER BY %s' % _ if _ else ''
dropdown = {'choices': mapping.keys(),
'selected_choice': sort if sort else 'relevance'}
query = sanitize_query(query)
results = SphinxTable.objects.db_manager('sphinx').raw("SELECT * FROM products WHERE MATCH('%(query)s') %(order_by)s LIMIT %(limit)s" % locals())
return render(request,
'search.html',
{'results': results,
'dropdown': dropdown})
template
<ul id="filter">
<li>
<h3>{{ dropdown.selected_choice }}▾</h3>
<ul>
{% for choice in dropdown.choices %}
{% if choice == dropdown.selected_choice %}
<li># {{ choice|capfirst }}</li>
{% else %}
<li>
<a href="{{ request.path }}?q={{ request.GET.q }}&sort={{ choice }}">
{{ choice|capfirst }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
</ul>

Slicing pagination in ListView?

I have successfully added pagination to a list_contacts view (obviously for a Contact model). And am now trying to setup the template context so that I can limit the pagination display to look like this :
< 1 ... 5 6 7 8 9 ... 42 >
I went from this answer and tried to use a "paginator" template tag, unsuccessfully. The main problem is that I'm using ListView instead of the old object_list, and these two don't set the context the same way.
Here's my view :
from django.views.generic import ListView
from app.models import Contact
list_contacts = ListView.as_view(
model=Contact,
http_method_names = ['get'],
template_name='contacts.html',
context_object_name='contacts',
paginate_by=6
)
And my template "contacts.html" :
<ul class="pagination">
{# Previous page link #}
{% if page_obj.has_previous %}
<li>
«
</li>
{% else %}
<li class="disabled">
«
</li>
{% endif %}
{# First page #}
{% if show_first %}
<li>
1
</li>
<li>...</li>
{% endif %}
{# List of pages (with current "active") #}
{% for page in page_numbers %}
{% ifequal page page_obj.number %}
<li class="active">
{{ page }}
</li>
{% else %}
<li>
{{ page }}
</li>
{% endifequal %}
{% endfor %}
{# Last page #}
{% if show_last %}
<li>...</li>
<li>{{ page_obj.pages }}</li>
{% endif %}
{# Next page link #}
{% if page_obj.has_next %}
<li>
»
</li>
{% else %}
<li class="disabled">
»
</li>
{% endif %}
</ul>
EDIT: here's my view code that ended up working
class ListContactsView(ListView):
model = Contact
http_method_names = ['get']
template_name = 'contacts.html'
context_object_name = 'contacts'
paginate_by = 6
def get_context_data(self, **kwargs):
_super = super(ListContactsView, self)
context = _super.get_context_data(**kwargs)
adjacent_pages = 2
page_number = context['page_obj'].number
num_pages = context['paginator'].num_pages
startPage = max(page_number - adjacent_pages, 1)
if startPage <= 3:
startPage = 1
endPage = page_number + adjacent_pages + 1
if endPage >= num_pages - 1:
endPage = num_pages + 1
page_numbers = [n for n in xrange(startPage, endPage) \
if n > 0 and n <= num_pages]
context.update({
'page_numbers': page_numbers,
'show_first': 1 not in page_numbers,
'show_last': num_pages not in page_numbers,
})
return context
list_contacts = ListContactsView.as_view()
There are different ways to achieve this (or even a combination of some):
Override or extend get_context_data by calling super
Write a templatetag
Write a template filter
Override or extend the PaginationMixin
An example of the first
class MyListView(ListView):
def get_context_data(self, **kwargs):
context = super(MyListView, self).get_context_data(**kwargs)
context['foo'] = 'bar'
return context

Django Conflict between 2 pagination

I'm trying to display all the data assoicated to every person and every house on an single template each with pagination.
The problem is everytime I view the entries of a particular data using pagination example person. The other pagination called house get reseted.
For example if I am on page 3 for house and I try to view other entries for page person . The house pagination will get reset back to 1. How do I fix this pagination conflict?
models
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100, blank=True)
def __unicode__(self):
return self.name
class House(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
views
def Display(request):
user= User.objects.get(username=request.user)
comment = Person.objects.get(user=user)
posts = House.objects.filter(user=user)
paginator = Paginator(comment, 5)
try: n = int(request.GET.get("n", '1'))
except ValueError: page = 1
try:
comment = paginator.page(n)
except (InvalidPage, EmptyPage):
comment = paginator.page(paginator.num_pages)
paginator = Paginator(posts, 5)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = paginator.page(paginator.num_pages)
return render(request,'display.html',{'posts':posts,'comment':comment})
HTML
{% for p in posts.object_list %}
{{p.name}}
{% endfor %}
{% if posts.object_list and posts.paginator.num_pages > 1 %}
<div class="pagination" style="margin-top: 20px; margin-left: -20px; ">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}<br>
{% if posts.has_previous %}
<a class="Link" href= "{% if formula %}?text={{formula}}&{% else %}?{% endif %}page={{ posts.previous_page_number }}">newer entries << </a>
{% endif %}
{% if posts.has_next %}
<a class="Link" href="{% if formula %}?text={{formula}}&{% else %}?{% endif %}page={{ posts.next_page_number }}"> >> older entries</a>
{% endif %}
</span>
</div>
{% endif %}
{% for c in comment.object_list %}
{{c.name}}
{% endfor %}
{% if comment.object_list and comment.paginator.num_pages > 1 %}
Page {{ comment.number }} of {{ comment.paginator.num_pages }}
{% if comment.has_previous %}
<a class="Link" href= "?n={{ comment.previous_page_number }}">newer entries << </a>
{% endif %}<br>
{% if comment.has_next %}
<a class="Link" href="?n={{ comment.next_page_number }}"> >> older entries</a>
{% endif %}
</span>
</div>
{% endif %
First off, your variable names versus how you describe everything is quite confusing.
The issue is you are using two different variables for the page: n for person (or comment) and page for house (or posts). In each of your <a href=""> you need to set both n and page not just one. So:
<a class="Link" href= "{% if formula %}?text={{formula}}&{% else %}?{% endif %}page={{ posts.previous_page_number }}&n={{ comment.number }}">newer entries << </a>
<a class="Link" href="{% if formula %}?text={{formula}}&{% else %}?{% endif %}page={{ posts.next_page_number }}&n={{ comment.number }}"> >> older entries</a>
<a class="Link" href= "?n={{ comment.previous_page_number }}&page={{ posts.number }}">newer entries << </a>
<a class="Link" href="?n={{ comment.next_page_number }}&page={{ posts.number }}"> >> older entries</a>
In the same view you change the value of paginator two times
paginator = Paginator(comment, 5)
# here you have code that does stuff and some lines below
paginator = Paginator(posts, 5)
if you want two paginators in the same view you should also name them differently (and add them to your context variables)
Something I can suggest is to have an ajax pagination view that you're going to call to switch between pages in your templates. In this way, you can change page to each resultset separately without needing to reload the entire view (and probably loosing your pagination state)