Paginación

Django proporciona formas de alto nivel y bajo nivel para ayudarte a gestionar datos paginados – es decir, datos divididos en varias páginas con enlaces «Anterior/Siguiente».

La clase Paginator

Bajo la cubierta, todas las métodos de paginación utilizan la Paginator clase. Ella hace todo el trabajo pesado de dividir realmente un QuerySet en objetos de página Page.

Ejemplo

Dale a Paginator una lista de objetos, más el número de elementos que deseas tener en cada página, y te da métodos para acceder a los elementos de cada página:

>>> from django.core.paginator import Paginator
>>> objects = ["john", "paul", "george", "ringo"]
>>> p = Paginator(objects, 2)

>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range)
<class 'range'>
>>> p.page_range
range(1, 3)

>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index()  # The 1-based index of the first item on this page
3
>>> page2.end_index()  # The 1-based index of the last item on this page
4

>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results

Nota

Ten en cuenta que puedes darle a Paginator una lista/tupla, un conjunto de consultas Django o cualquier otro objeto con un método count() o __len__(). Cuando determina el número de objetos contenidos en el objeto pasado, Paginator primero intentará llamar a count(), luego caerá hacia atrás a utilizar len() si el objeto pasado no tiene un método count(). Esto permite que los objetos como Django’s QuerySet utilicen un método de conteo más eficiente cuando esté disponible.

Paginando en ListView

django.views.generic.list.ListView proporciona una forma de paginar la lista mostrada de manera integrada. Puedes hacer esto agregando un atributo paginate_by a tu clase de vista, por ejemplo:

from django.views.generic import ListView

from myapp.models import Contact


class ContactListView(ListView):
    paginate_by = 2
    model = Contact

Esto limita el número de objetos por página y agrega un paginator y page_obj al context. Para permitir que tus usuarios naveguen entre páginas, agrega enlaces a la siguiente y anterior página en tu plantilla de esta manera:

{% for contact in page_obj %}
    {# Each "contact" is a Contact model object. #}
    {{ contact.full_name|upper }}<br>
    ...
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">next</a>
            <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

Usando Paginator en una función de vista

Aquí tienes un ejemplo usando Paginator en una función de vista para paginar un conjunto de objetos:

from django.core.paginator import Paginator
from django.shortcuts import render

from myapp.models import Contact


def listing(request):
    contact_list = Contact.objects.all()
    paginator = Paginator(contact_list, 25)  # Show 25 contacts per page.

    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    return render(request, "list.html", {"page_obj": page_obj})

En la plantilla list.html, puedes incluir navegación entre páginas de la misma manera que en la plantilla del ListView anterior.