what - Mostrar solo algunos de los números de página por paginación django
projects django (6)
Estoy usando el paginador de django en la plantilla. Está funcionando bien, pero no es bueno cuando hay un gran número de páginas.
views.py:
def blog(request):
blogs_list = Blog.objects.all()
paginator = Paginator(blogs_list, 1)
try:
page = int(request.GET.get(''page'', ''1''))
except:
page = 1
try:
blogs = paginator.page(page)
except(EmptyPage, InvalidPage):
blogs = paginator.page(page)
return render(request, ''blogs.html'', {
''blogs'':blogs
})
Fragmento de la plantilla:
<div class="prev_next">
{% if blogs.has_previous %}
<a class="prev btn btn-info" href="?page={{blogs.previous_page_number}}">Prev</a>
{% endif %}
{% if blogs.has_next %}
<a class="next btn btn-info" href="?page={{blogs.next_page_number}}">Next</a>
{% endif %}
<div class="pages">
<ul>
{% for pg in blogs.paginator.page_range %}
{% if blogs.number == pg %}
<li><a href="?page={{pg}}" class="btn btn-default">{{pg}}</a></li>
{% else %}
<li><a href="?page={{pg}}" class="btn">{{pg}}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
<span class="clear_both"></span>
</div>
Ahora se ve así:
¿Qué hago para mostrar solo 7 números de página y no todos ellos desde el número de página actual, como esto:
Prev 1 (2) 3 4 5 Next
Espero que haya quedado claro, si no pregunta por favor. Su ayuda y orientación serán muy apreciadas. Gracias.
Descubrí que lo más sencillo era crear un fragmento de paginación que solo muestra las páginas que desea.
En mi caso no quería ningún enlace anterior o siguiente. Solo quería tener siempre un enlace a la primera y la última página y luego tener la página actual y las dos páginas a cada lado de la página actual.
Mi fragmento de plantilla (usa variables de django-tables2; las variables tendrán nombres ligeramente diferentes si estás usando un Paginator
Django directamente)
{% load django_tables2 %}
{% load humanize %}
{% load i18n %}
{% if table.page %}
{% with table.page.paginator.count as total %}
{% with table.page.number as page_num %}
{% with table.page.paginator.num_pages as num_pages %}
{% block pagination %}
<div class="row">
<div class="col-md-12">
{% if table.paginator.num_pages > 1 %}
<ul class="pagination pull-right">
{% for n in table.page.paginator.page_range %}
{% if table.page.number|add:''-3'' == n %}
{# First page #}
<li><a href="{% querystring table.prefixed_page_field=1 %}">1</a></li>
{% if n != 1 %}
<li class="disabled"><a>⋯</a></li>
{% endif %}
{% elif table.page.number == n %}
{# Current page #}
<li class="active"><a href="#">{{ n }}</a></li>
{% elif table.page.number|add:''-3'' < n and n < table.page.number|add:''3'' %}
{# Pages around current page #}
<li><a href="{% querystring table.prefixed_page_field=n %}">{{ n }}</a></li>
{% elif table.page.number|add:''3'' == n %}
{# Last page #}
{% if n != num_pages %}
<li class="disabled"><a>⋯</a></li>
{% endif %}
<li><a href="{% querystring table.prefixed_page_field=num_pages %}">{{ num_pages }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock pagination %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}
Ejemplos de cómo se ve mi paginación en diferentes páginas
Crédito: esto se inspiró en la respuesta de @Pavel1114
En primer lugar me gustaría cambiar lo siguiente:
try:
blogs = paginator.page(page)
except(EmptyPage, InvalidPage):
blogs = paginator.page(page) # Raises the same error
Pero podrías pasar un rango dentro de tu contexto.
index = paginator.page_range.index(blogs.number)
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]
Ahora deberías poder recorrer el rango para construir los enlaces correctos con ?page=
.
=== Editar ===
Entonces tu vista sería algo como esto:
def blog(request):
paginator = Paginator(Blog.objects.all(), 1)
try:
page = int(request.GET.get(''page'', ''1''))
except:
page = 1
try:
blogs = paginator.page(page)
except(EmptyPage, InvalidPage):
blogs = paginator.page(1)
# Get the index of the current page
index = blogs.number - 1 # edited to something easier without index
# This value is maximum index of your pages, so the last page - 1
max_index = len(paginator.page_range)
# You want a range of 7, so lets calculate where to slice the list
start_index = index - 3 if index >= 3 else 0
end_index = index + 3 if index <= max_index - 3 else max_index
# Get our new page range. In the latest versions of Django page_range returns
# an iterator. Thus pass it to list, to make our slice possible again.
page_range = list(paginator.page_range)[start_index:end_index]
return render(request, ''blogs.html'', {
''blogs'': blogs,
''page_range'': page_range,
})
Así que ahora tenemos que editar su plantilla para aceptar nuestra nueva lista de números de página:
<div class="prev_next">
{% if blogs.has_previous %}
<a class="prev btn btn-info" href="?page={{blogs.previous_page_number}}">Prev</a>
{% endif %}
{% if blogs.has_next %}
<a class="next btn btn-info" href="?page={{blogs.next_page_number}}">Next</a>
{% endif %}
<div class="pages">
<ul>
{% for pg in page_range %}
{% if blogs.number == pg %}
<li><a href="?page={{pg}}" class="btn btn-default">{{pg}}</a></li>
{% else %}
<li><a href="?page={{pg}}" class="btn">{{pg}}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
<span class="clear_both"></span>
</div>
Lo hice solo en plantillas con expresiones:
{% if is_paginated %}
<div class="text-center">
<ul class="pagination pagination-sm">
{% if page_obj.number >= 5 %}
<li><a href="?page=1">1</a></li>
<li><span>...</span></li>
{% elif page_obj.number == 4 %}
<li><a href="?page=1">1</a></li>
{% endif %}
{% if page_obj.number|add:"-2" >= 1 %}
<li><a href="?page={{ page_obj.number|add:"-2" }}">{{ page_obj.number|add:"-2" }}</a></li>
{% endif %}
{% if page_obj.number|add:"-1" >= 1 %}
<li><a href="?page={{ page_obj.number|add:"-1" }}">{{ page_obj.number|add:"-1" }}</a></li>
{% endif %}
<li class="active"><a href="?page={{ page_obj.number }}">{{ page_obj.number }}</a></li>
{% if page_obj.number|add:"1" <= paginator.num_pages %}
<li><a href="?page={{ page_obj.number|add:"1" }}">{{ page_obj.number|add:"1" }}</a></li>
{% endif %}
{% if page_obj.number|add:"2" <= paginator.num_pages %}
<li><a href="?page={{ page_obj.number|add:"2" }}">{{ page_obj.number|add:"2" }}</a></li>
{% endif %}
{% if page_obj.number|add:"2" <= paginator.num_pages|add:"-2" %}
<li><span>...</span></li>
<li><a href="?page={{ paginator.num_pages }}">{{ paginator.num_pages }}</a></li>
{% elif page_obj.number|add:"1" <= paginator.num_pages|add:"-2" %}
<li><a href="?page={{ paginator.num_pages }}">{{ paginator.num_pages }}</a></li>
{% endif %}
</ul>
</div>
{% endif %}
Sé que django es como "no vuelvas a escribir tu código", pero me parece más fácil de entender ahora. Espero haberte ayudado.
Otra solución más corta con plantilla es comparar forloop.counter actual con cierto rango.
con bootstrap utilizo esta plantilla
<nav aria-label="Page navigation"> <ul class="pagination"> {% if page_obj.has_previous %} <li class="page-item"> <a class="page-link" href="?page=1" aria-label="Previous"> <span aria-hidden="true">«</span> <span class="sr-only">begin</span> </a> </li> {% endif %} {% for n in page_obj.paginator.page_range %} {% if page_obj.number == n %} <li class="page-item active"> <span class="page-link">{{ n }}<span class="sr-only">(current)</span></span> </li> {% elif n > page_obj.number|add:''-3'' and n < page_obj.number|add:''3'' %} <li class="page-item"><a class="page-link" href="?page={{ n }}">{{ n }}</a></li> {% endif %} {% endfor %} {% if page_obj.has_next %} <li class="page-item"> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Next"> <span aria-hidden="true">»</span> <span class="sr-only">end</span> </a> </li> {% endif %} </ul> </nav>
También podrías extender la clase Paginator
.
class BootstrapPaginator(Paginator):
def __init__(self, *args, **kwargs):
"""
:param wing_pages: How many pages will be shown before and after current page.
"""
self.wing_pages = kwargs.pop(''wing_pages'', 3)
super(BootstrapPaginator, self).__init__(*args, **kwargs)
def _get_page(self, *args, **kwargs):
self.page = super(BootstrapPaginator, self)._get_page(*args, **kwargs)
return self.page
@property
def page_range(self):
return range(max(self.page.number - self.wing_pages, 1),
min(self.page.number + self.wing_pages + 1, self.num_pages + 1))
Luego en plantilla:
{% for num in action_list.paginator.page_range %}
{% if action_list.number == num %}
<li class="active"><a href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li><a href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
Ahora page_range
solo constará de 7 elementos. wing_pages
+ página actual + wing_pages
personalizado django paginator para bootstrap
Voy a lanzar esto. Se me ocurrió porque te permite saber que hay más páginas en cada lado.
<ul class="pagination">
{% if page_obj.has_previous %}
<li><a href="?page={{ page_obj.previous_page_number }}"><i class="fa fa-chevron-left" aria-hidden="true"></i></a></li>
{% else %}
<li class="disabled"><span><i class="fa fa-chevron-left" aria-hidden="true"></i></span></li>
{% endif %}
{% if page_obj.number|add:''-4'' > 1 %}
<li><a href="?page={{ page_obj.number|add:''-5'' }}">…</a></li>
{% endif %}
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% elif i > page_obj.number|add:''-5'' and i < page_obj.number|add:''5'' %}
<li><a href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.paginator.num_pages > page_obj.number|add:''4'' %}
<li><a href="?page={{ page_obj.number|add:''5'' }}">…</a></li>
{% endif %}
{% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}"><i class="fa fa-chevron-right" aria-hidden="true"></i></a></li>
{% else %}
<li class="disabled"><span><i class="fa fa-chevron-right" aria-hidden="true"></i></span></li>
{% endif %}
</ul>
Y se parece a esto: