Как сделать простой пагинатор на Django и HTMx. Добавляем сортировку и фильтры ч. 2
08.04.2025
15.04.2025
9 минут
48
0
0
0
0
Вступление
Любой пагинатор хорош на столько, на сколько хороши его фильтры. В этой статье я покажу как можно "органически" встроить любые фильтры и сортировку на твой пагинатор. Этот пагинатор будет написан с использованием HTMx. Который в свою очередь будет общаться с API нашего сайта, настроенный при помощи Django REST framework.
Фильтры и сортировщики, которые мы добавим к пагинатору будут статическими. Что я имею в виду. Под статическими фильтрами и сортировщиками я имею в виду, те фильтры, для применения которых потребуется нажать кнопку сортировки. То есть, сначала выбираем необходимые опции, потом жмякаем соответствующую кнопку.
Я мог бы, сделать его динамическим, но для этого потребуется писать дополнительный JS-код и сильно усложнять реализацию пагинатора и это уже не будет статья про "добавление фильтров к простому пагинатору". Не о том статья.
То, как будут выглядеть фильтры на странице
Пишем фронтенд
Наши фильтры и сортировщики располагаются в одном месте и обрамлены тегом form. Принципиальной разницы между ими нет. По крайней мере, на стороне клиента оформляться они будут одинаково.
Создадим шаблон, где будут отрисовываться наши фильтры и сортировщики. Я назвал его paginator-filters.html и разместил в parts/ директории. Сейчас разберём что же там такое написано. Вот полный код шаблона:
В этой форме две кнопки. Одна отправляет запрос с параметрами, другая отправляет запрос без параметров, то есть очищает фильтры.
Та, что выделена отправляет запрос с параметрами, то есть фильтрует выдачу
С тем, как и откуда отправляются запросы о фильтрации и сортировки мы разобрались. Разберём теперь что, и как мы отправляем на сервер.
В самом начале есть скрытое поле ввода, которое содержит номер страницы. Он всегда 1 кстати. Почему начинать фильтрацию с первой страницы, а не, скажем, с текущей? Вообще это возможно сделать, просто это приведёт к сильному усложнению кода на сервере и лишним проверкам на то, чтобы не выйти из-за границ возможного. Например, нам доступно только 5 страниц пагинации, по результатам фильтрации, а мы сейчас на 6-й.
После номера страницы, форма разделена (условно, конечно) на часть фильтров и часть сортировщиков. Давай сначала посмотрим на сортировщики:
Первое, что хочу отметить это переменные шаблонов, такие как alphabetic_sort, last_pub_sort и last_upd_sort. Они определяются и возвращаются сервером. И если они определены, то поля ввода будут уже активны. Так же в input элементе я определяю имя(name) отправляемого параметра, оно же и будет именем переменной в шаблоне.
С сортировщиками всё, перейдём к фильтрам. В блоке фильтров ты можешь найти два их типа:
Простые, это те которые работают точно так же как и сортировщики. Такая же разметка.
Сложные, это те, которые должны будут сделать предварительный запрос на сервер, чтобы узнать, какие элементы фильтрации отобразить.
Пример сложного фильтра.
Я делаю GET-запрос, по адресу "api/epochs/" используя hx-get атрибут. Атрибут hx-tirgger, указывает когда, нужно сделать данный запрос. И указывается целевой элемент в который будет происходить загрузка ответа с сервера, данный целевой элемент указывается при помощи hx-target атрибут.
Как можно заметить, я обращаюсь к API моего сайта, чтобы получить все возможные варианты фильтрации по данному критерию, то есть эпохи. Чтобы их отрендерить, потребуется написать соответствующий шаблон, назовём его paginator-filter-items.html и положим его в parts/ директорию.
Этот шаблон отрисует все имеющиеся элементы фильтрации и пометит активные поля ввода.
С фронтендом мы почти закончили, нужно лишь обновить файл paginator-buttons.html. И в каждом атрибуте hx-post, в конец, добавить следующую переменную шаблона, base_url. Это делается для того, чтобы при перемещении между страницами сохранялась фильтрация и сортировка.
Вот обновлённый файл:
Жёлтым помечены те строки, где произошли изменения.
Пишем бэкенд
Вся суть клиентской части фильтров и сортировщиков состоит в том, чтобы построить соответствующий URL и отправить его на сервер, где сервер уже разбирается что и как вернуть пользователю.
Всё это будет происходить в ранее написанном классе-представлении HomeView, в файле Frontend/views.py. Который мы подключили в Frontend/urls.py.
Теперь взглянем, как я изменил HomeView класс:
В данном файле я добавил создание базового URL адреса, который присоединяется к hx-post атрибуту, в кнопках пагинации. Работает он довольно просто. Берёт адрес запроса, разбивает его на пары ключ-значение и соединяет их обратно, но только без указания номера страницы.
Ой и чуть не забыл. Я так же добавил логику фильтрации и сортировки статей. Она в самом начале. Пройдёмся по порядку:
Здесь, я извлекаю сам запрос переменными в URL-адресе.
Тут, происходит базовая фильтрация и сортировка по дате публикации (Сначала отображаются самые новые статьи).
После я провожу непосредственную фильтрацию и сортировку по тем параметрам, которые были отправлены через URL.
Данная сортировка работает по принципу "трубы". То есть, каждый последующий фильтр работает с выходными данными предыдущего фильтра. Таким образом остаются только те статьи, которые прошли через все фильтры.
Ещё нужно рассмотреть такой класс-представление как FiltersView. Это представление, которое работает и возвращает html-странички со списком всех доступных фильтров по записям в моделях Figure, Epoch, Period и Category.
Вот его код FiltersView класса:
Основной задачей данного представления является "фильтрация фильтров" по языку и пометкой их при необходимости в шаблоне (то есть, помечать их чекнутыми если они уже используются). Его ещё нужно подключить к маршрутизатору в Backend/urls.py:
При подключении FiltersView, нужно указать следующие параметры:
Имя параметра (оно будет использоваться в URL)
Класс сериализатора
Список объектов с которыми будет работать FilterView класс
Выводы
Вот, что я хотел бы сказать, по поводу фильтров на страницах пагинатора. Это не сложно, если не усложнять и придумывать колесо заново. Как ты мог заметить, я использовал HTMx по минимуму, а REST API почти не использовал, потому что это не требовалось. Мы создаём простой пагинатор, а не сложно-универсальный убер-пупер ультимативный поиск. Вот.
В этой статье я на примере своего сайта покажу, что такое дублированный(не канонический) контент появившийся в Google search console в результате внедрения пагинатора или бесконечной ленты. Со статистикой и графикой. …
В этой статье я опишу способ, как можно реализовать асинхронную загрузку контента(статей) при помощи кнопки "Больше", которая сделана при помощи Django, REST API, HTMx и стилизовано при помощи DaisyUI
В этой статье я опишу то, как создать пагинатор используя Django и HTMx библиотеку. И то, почему это было так просто в сравнении с пагинатором на моём сайте. С шаблонами …
В этой статье я опишу процесс и основные блоки кода, для того чтобы добавить сортировку и фильтрацию к пагинатору. Данный пагинатор написан на Django используя HTMx.
В этой статье, я опишу как добавить на ваш Django-сайт форму обратной связи используя HTMx и немного DaisyUI, в качестве UI-библиотеки. Всё будет сделано на примере моего нового сайта. Но …
В этой статье я опишу процесс кастомизации таких страниц как 404 и 500. Я покажу два основных способа как это сделать и то как можно быстро и легко настроить сервер …
Использованные термины
Пагинация ⟶ Это паттерн дизайна сайтов, который подразумевает разделение контента сайта на отдельные страницы.
Джанго представления ⟶ Это функции или классы, которые обрабатывают HTTP-запросы и возвращают HTTP-ответы. Они отвечают за бизнес-логику вашего приложения и связаны с моделями данных, чтобы взять информацию из базы данных и отобразить её пользователю.
HTML (Гипер текстовый язык разметки) ⟶ Это стандартный язык разметки, используемый для создания веб-страниц. Он описывает структуру и содержание документа с помощью различных элементов и тегов. HTML позволяет интегрировать текст, изображения, ссылки, формы и другие медиа-элементы.
CORS (Cross-Origin Resource Sharing) ⟶ Это механизм, который позволяет ограничить ресурсы веб-страниц, чтобы они могли запросить ресурсы с других доменов. Поскольку правила безопасности браузеров по умолчанию блокируют такие запросы (из-за политики одинаковых источников), CORS предоставляет способ более безопасного доступа к ресурсам с других источников.
Релевантные вопросы
Почему, создавая сайт ты предпочёл django, а не конструкторы сайтов ?
Довольно сложный вопрос. Наверное я люблю разбираться и копаться в сложных вещах, хотя вроде бы и не стоило. Тут похожая история с играми. Вместо того, чтобы делать игры на игровых движках, я делал свой собственный, **DI**. Ну, я просто не ищу простых путей ;)
В чём отличие OneToOne от ForeingKey полей ?
Их основным и главным отличием является то, как они относятся к модели на которую они ссылаются. Если отношение между моделью с полем OneToOne и целевой моделью, такие, что может быть только одна такая запись в БД, то ForeignKey допускает не ограниченное количество оных.
Когда использовать поле связи OneToOne в Django моделях ?
Данное поле модели отлично подойдёт, когда необходимо добавить новый функционал к уже существующей модели (или вернее сказать, к записи модели в БД). То есть, тогда, когда менять существующий функционал уже созданных моделей не оправдано тяжело или дорого.