Постраничная навигация (пагинация)
На этом занятии продолжим совершенствовать наш учебный сайт и рассмотрим следующий вопрос – постраничную навигацию. Иногда еще говорят «пагинация» (от лат. pagina – страница). Но это все о том, как на сайте представлять длинные списки чего-либо. Вот классический пример постраничной навигации:
Здесь длинный список разбивается на блоки по 10 задач и внизу следуют номера страниц для отображения остальных номеров. Это, в некотором смысле, рекомендуемая практика. Так HTML-страница получается не слишком большой по объему и пользователю удобнее ориентироваться в данных списка.
Если на странице официальной документации
набрать запрос «pagination», то сразу увидим первые две подходящие ссылки. По первой ссылке представлен пример работы класса пагинатора:
А по второй – API данного класса (набор методов и свойств для работы с ним):
Советую также со всем этим внимательно ознакомиться. А мы рассмотрим пример работы класса Paginator на конкретном примере. Предположим, имеется следующий список имен известных женщин:
И нам нужно выводить их постранично, допустим, по три имени на странице. Для этого, вначале, выполним импорт класса:
и создадим его экземпляр:
Все, наш пагинатор готов к использованию. Например:
Чтобы получить первую страницу со списком имен, используется метод:
Я не буду приводить здесь полный список свойств и методов объекта пагинации, вы все это легко сможете посмотреть на странице документации по указанным ссылкам. Лучше давайте посмотрим, как все это можно использовать непосредственно на сайте.
Как вы помните, за отображение списка у нас отвечает базовый класс ListView. В этот класс уже встроен механизм постраничного вывода списков. Согласно документации, все что нам нужно, это определить атрибут:
указав число N – количество элементов на одной странице.
При использовании функций представлений, нужно уже самостоятельно создавать объект класса Paginator, получать номер текущей страницы и для нее формировать объект с набором элементов. Ниже в документации это все приведено, поэтому, здесь я покажу короткий пример, как можно этим воспользоваться в рамках нашего сайта.
Смотрите, у нас есть функция представления about(). Давайте в нее добавим следующие строчки:
Я их, фактически, скопировал из документации, прописал нашу модель Women и указал отображать 3 элемента на странице. Далее, коллекцию page_obj для текущей страницы нужно передать в шаблон:
А в самом шаблоне about.html, перебрать эту коллекцию и отобразить на странице:
Как видите, все предельно просто. Если теперь открыть страницу «О сайте», то увидим первые три имени из модели Women. Чтобы перейти на следующую страницу, нужно в запросе указать специальный параметр page с номером страницы, например, так:
Именно этот параметр читается из словаря GET и подставляется в метод get_page для получения соответствующей страницы. Причем, указывая не существующий номер, будет отображаться последняя страница. Если вместо номера указать, например, буквы, то параметр игнорируется и отображается первая страница.
Осталось отобразить ссылки на номера страниц непосредственно в шаблоне. Для этого мы через объект page_obj обратимся к объекту paginator и у него укажем метод page_range():
Обратите внимание, в шаблонах у методов мы не пишем круглые скобки, весь вызов берет на себя шаблонизатор. Итак, метод page_range() возвращает итератор для перебора номеров страниц. Поэтому в шаблоне мы можем воспользоваться тегом for для формирования номеров страниц:
Здесь все достаточно очевидно, мы перебираем доступные номера, формируем ссылку с параметром page и текущим номером. Именем ссылки также выступает ее номер. Если теперь перейти в браузер и обновить страницу, то увидим номера страниц.
Я здесь ничего улучшать пока не буду, это был пример того, как можно использовать пагинацию совместно с функциями представлений. Давайте теперь перейдем к классам и посмотрим как подобный функционал реализовать на главной странице.
Для этого в дочернем классе WomenHome достаточно прописать атрибут:
Получим разбивку по три поста на страницу. Если теперь открыть главную страницу, то именно первые три записи мы и увидим. Но у нас пока нет отображения номеров страниц. Давайте добавим их. Для этого на уровне шаблонов у нас появляется специальные объекты paginator и page_obj. Воспользуемся объектом paginator и отобразим в шаблоне список страниц. Я это сделаю в файле base.html, чтобы страницы отображались во всех шаблонах, если они есть. Значит, в файле base.html после блока контента (<% block content %><% endblock %>) пропишем следующие уже знакомые нам строчки:
Только здесь, в отличие от шаблона about.html, мы используем объект paginator напрямую, т.к. класс ListView его автоматически добавляет в шаблон. Из файла about.html уберем вывод списка страниц, т.к. теперь в этом нет необходимости.
Отлично, если теперь обновить главную страницу сайта, то увидим номера страниц в виде списка вместе со стилями, которые подключены через классы оформлений list-pages и page-num. Сами стили я дополнительно определил в файле styles.css и, если интересно, то вы сможете их самостоятельно посмотреть в проекте, скачав его с github.
Итак, главная страница отображается в нужном нам виде, но если открыть отдельные рубрики, то пагинация пропадает. Почему? Если мы вернемся в проект, то увидим, что за это отвечает отдельный класс WomenCategory, в котором, по идее, также нужно прописать
Однако, это будет дублирование кода и нам проще эту строчку записать в общем классе DataMixin:
А из класса WomenHome ее убрать. Теперь, на всех страницах, унаследованных от ListView, мы будем видеть автоматическую пагинацию. Правда, везде будет отображаться по три статьи. В данном случае нас это устраивает, но если на разных страницах нужно разное число рубрик при пагинации, то, конечно, в каждом классе отдельно следует прописать атрибут paginate_by со своим значением.
Однако, если сейчас в paginate_by указать большое число, например, 30, то на главной странице будет отображаться внизу один единственный номер, что не очень хорошо. Было бы лучше, при одной единственной странице вообще не выводить никаких номеров. Для этого, в шаблоне base.html вывод пагинации будем осуществлять по следующему условию:
Все, теперь в базовом варианте отображение номеров страниц сделано. Далее, сделаем некоторые улучшения. Например, нам следовало бы убирать ссылку на текущий номер страницы. Для этого в шаблоне base.html мы, при выводе ссылок, будем проверять, если это текущий номер, то отображать его как текст:
Здесь используется свойство number объекта page_obj для определения номера текущей страницы, и если этот номер равен p, то его следует отобразить как текст. Иначе, идет отображение в виде ссылки.
Следующая проблема, с котором мы можем столкнуться – это большое число номеров при большом числе записей. И при навигации это будет приводить к некоторым неудобствам, да и на дизайне страницы большое число номеров сказывается не лучшим образом. Поэтому, на практике, ограничиваются небольшим числом отображаемых номеров слева и справа от текущего номера. Вот это мы сейчас и сделаем.
Для начала, в админ-панели отметим все статьи для публикации (чтобы номеров страниц было побольше) и в классе DataMixin установим атрибут:
Теперь на главной странице у нас появляется семь номеров. Но, мы решили отображать не все их, а, допустим, два справа и два слева:
Этот диапазон, на уровне шаблонов, можно определить через фильтр add, уменьшая значение на 2 и увеличивая на 2 относительно текущего номера страницы. Поэтому в цикле, при отображении этих номеров, мы можем записать следующее условие вывода:
Все, теперь, обновляя страницу сайта, мы увидим по два номера слева и справа.
Следующим нашим улучшением будет отображение ссылок для перехода на предыдущую и следующую страницы. Это делается очень просто. В шаблоне base.html до цикла запишем такие строчки:
Мы здесь вначале проверяем, имеет ли смысл вообще выводить ссылку на предыдущую страницу (для первых номеров это не нужно делать). И, если она нужна, то отображаем как элемент списка с номером страницы согласно атрибуту previous_page_number.
То же самое делаем и для отображения ссылки на следующую страницу. Только теперь добавляем пункт после цикла for:
Здесь также вначале идет проверка: нужно ли отображать ссылку. Если проверка проходит, то отображаем пункт списка со ссылкой согласно атрибуту next_page_number.
Все, можно обновить главную страницу сайта и увидим эти дополнительные элементы управления. Вот так, в базовом варианте, можно работать с пагинацией во фреймворке Django.
Видео по теме
#1. Django — что это такое, порядок установки
#2. Модель MTV. Маршрутизация. Функции представления
#3. Маршрутизация, обработка исключений запросов, перенаправления
#4. Определение моделей. Миграции: создание и выполнение
#5. CRUD — основы ORM по работе с моделями
#6. Шаблоны (templates). Начало
#7. Подключение статических файлов. Фильтры шаблонов
#8. Формирование URL-адресов в шаблонах
#9. Создание связей между моделями через класс ForeignKey
#10. Начинаем работу с админ-панелью
#11. Пользовательские теги шаблонов
#12. Добавляем слаги (slug) к URL-адресам
#13. Использование форм, не связанных с моделями
#14. Формы, связанные с моделями. Пользовательские валидаторы
#15. Классы представлений: ListView, DetailView, CreateView
#16. Основы ORM Django за час
#17. Mixins — убираем дублирование кода
#18. Постраничная навигация (пагинация)
#19. Регистрация пользователей на сайте
#20. Делаем авторизацию пользователей на сайте
#21. Оптимизация сайта с Django Debug Toolbar
#22. Включаем кэширование данных
#23. Использование капчи captcha
#24. Тонкая настройка админ панели
#25. Начинаем развертывание Django-сайта на хостинге
#26. Завершаем развертывание Django-сайта на хостинге
© 2021 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Источник
Пагинация в Django-Rest-Framework с использованием API-View
В настоящее время у меня есть настройка представления API следующим образом:
Здесь CartDetailSerializer — обычный ModelSerializer.
Я хочу разбить этот API на страницы. Однако в документах DRF я нашел это:
Если вы используете обычный APIView, вам нужно будет самостоятельно вызвать API разбиения на страницы, чтобы гарантировать, что вы вернете разбитый на страницы ответ.
Нет примера того, как разбить обычный API APIView на страницы.
Может ли кто-нибудь опубликовать пример, который я могу использовать в приведенном выше сценарии.
4 ответа
При использовании обычного APIView вам необходимо использовать собственный класс Django Paginator.
В вашем случае вы можете разбить запрос на страницы перед отправкой его в сериализатор.
Что-то вроде этого:
Надеюсь это поможет.
Я использую DRF версии 3.6.2. Вам не нужно так много писать. Просто используйте эти простые шаги.
Если вам нужна функция поиска с помощью метода получения, вы можете написать ниже код
Я предпочитаю расширять класс Paginator, вот как это будет выглядеть:
Хотя то, о чем упоминает Рэй, возможно, django-rest-framework может справиться с этим внутренне с некоторыми дополнительными функциями, которые значительно упрощают работу с вашим API. (* обратите внимание, что разбиение на страницы django-rest-framework построено из пагинатора Django из django.core.paginator)
Сразу после того, что вы процитировали, есть ключевая информация для решения этой проблемы:
Разбивка на страницы выполняется автоматически только в том случае, если вы используете общие представления или наборы представлений. Если вы используете обычный APIView, вам нужно будет самостоятельно вызвать API разбиения на страницы, чтобы гарантировать, что вы вернете разбитый на страницы ответ. Пример см. в исходном коде классов mixins.ListMixin и generics.GenericAPIView.
Небольшая поправка к тому, что там сказано: посмотрите на ListModelMixin.
Если вы перейдете по этим двум ссылкам, вы увидите исходный код для вышеуказанных файлов: generics.py mixins.py
Что вам нужно сделать, так это включить что-то вроде следующего, чтобы разбивка на страницы работала в APIView (** примечание: этот код не протестирован, но идея верна. Также есть лучший способ написать это, а не включать код во всех отношениях, но я оставлю это на ваше усмотрение, чтобы мой ответ был кратким и понятным):
Я надеюсь, что это больше помогло вам и другим, кто наткнулся на этот пост.
Источник