Введение в пагинацию. Класс Paginator

Курс по Django: https://stepik.org/a/183363

Архив проекта: 57_sitewomen.zip

Продолжаем совершенствовать наш учебный сайт и сделаем следующее улучшение – постраничную навигацию. Иногда еще говорят «пагинация» (от лат. pagina – страница). Но это все о том, как на сайте представлять длинные списки чего-либо. Вот классический пример постраничной навигации:

Здесь длинный список разбивается на блоки по 10 задач и внизу следуют номера страниц для отображения остальных номеров. Это, в некотором смысле, рекомендуемая практика. Так HTML-страница получается не слишком большой по объему и пользователю удобнее ориентироваться в данных списка.

Если на странице официальной документации

https://docs.djangoproject.com/en/4.2/

набрать запрос «pagination», то сразу увидим первые две подходящие ссылки. По первой ссылке представлен пример работы класса пагинатора:

https://docs.djangoproject.com/en/4.2/topics/pagination/

А по второй – API данного класса (набор методов и свойств для работы с ним):

https://docs.djangoproject.com/en/4.2/ref/paginator/

Советую со всем этим внимательно ознакомиться. А мы рассмотрим пример работы класса Paginator. Предположим, имеется следующий список имен известных женщин:

women = ['Анджелина Джоли', 'Дженнифер Лоуренс', 'Джулия Робертс', 'Марго Робби', 'Ума Турман', 'Ариана Гранде', 'Бейонсе', 'Кэтти Перри', 'Рианна', 'Шакира']

И нам нужно выводить их постранично, допустим, по три имени на странице. Для этого, вначале, выполним импорт класса:

from django.core.paginator import Paginator

и создадим его экземпляр:

p = Paginator(women, 3)

Все, наш пагинатор готов к использованию. Например:

p.count # число элементов в списке
p.num_pages # число страниц (10:3 = 4 – округление до большего)
p.page_range # итератор для перебора номеров страниц

Чтобы получить первую страницу со списком имен, используется метод:

p1 = p.page(1) # получение первой страницы
p1.object_list  # список элементов текущей страницы
p1.has_next() # имеется ли следующая страница
p1.has_previous() # имеется ли предыдущая страница
p1.has_other_pages() # имеются ли вообще страницы
p1.next_page_number() # номер следующей страницы
p1.previous_page_number() # номер предыдущей страницы

Я не буду приводить здесь полный список свойств и методов объекта пагинации, вы все это легко сможете посмотреть на странице документации по указанным ссылкам. Лучше давайте посмотрим, как все это можно использовать непосредственно на сайте.

Если применяются функции представления, то нужно самостоятельно создавать объект класса Paginator, получать номер текущей страницы и для нее формировать объект с набором элементов. Ниже в документации это все приведено, поэтому, здесь я покажу короткий пример, как можно этим воспользоваться в рамках нашего сайта.

Смотрите, у нас есть функция представления about(). Давайте в нее добавим следующие строчки:

    contact_list = Women.published.all()
    paginator = Paginator(contact_list, 3)
 
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

Я их, фактически, скопировал из документации, прописал нашу модель Women и указал отображать 3 элемента на странице. Далее, коллекцию page_obj для текущей страницы нужно передать в шаблон:

return render(request, 'women/about.html', {'page_obj': page_obj, 'title': 'О сайте'})

А в самом шаблоне about.html, перебрать эту коллекцию и отобразить на странице:

{% for contact in page_obj %}
{{ contact }}</p>
{% endfor %}

Как видите, все предельно просто. Если теперь открыть страницу «О сайте», то увидим первые три имени из модели Women. Чтобы перейти на следующую страницу, нужно в запросе указать специальный параметр page с номером страницы, например, так:

http://127.0.0.1:8000/about/?page=2

Именно этот параметр читается из словаря GET и подставляется в метод get_page для получения соответствующей страницы. Причем, указывая не существующий номер, будет отображаться последняя страница. Если вместо номера указать, например, буквы, то параметр игнорируется и показываются первые три записи.

Осталось отобразить ссылки на номера страниц непосредственно в шаблоне. Для этого мы через объект page_obj обратимся к объекту paginator и у него укажем объект-свойство page_range:

page_obj.paginator.page_range

Обратите внимание, в шаблонах у методов мы не пишем круглые скобки, весь вызов берет на себя шаблонизатор. Итак, page_range возвращает итератор для перебора номеров страниц. Поэтому в шаблоне мы можем воспользоваться тегом for для формирования номеров страниц:

<nav>
    <ul>
        {% for p in page_obj.paginator.page_range %}
        <li>
            <a href="?page={{ p }}">{{ p }}</a>
        </li>
        {% endfor %}
    </ul>
</nav>

Здесь все достаточно очевидно, мы перебираем доступные номера, формируем ссылку с параметром page и текущим номером. Именем ссылки также выступает ее номер. Если теперь перейти в браузер и обновить страницу, то увидим номера страниц.

Я здесь ничего улучшать пока не буду, это был пример того, как можно использовать пагинацию совместно с функциями представлений. На следующем занятии мы сделаем пагинацию для всех наших списков с их красивым оформлением.

Курс по Django: https://stepik.org/a/183363

Видео по теме