Как сделать простой пагинатор на Django и HTMx ч. 1

Часы
02.04.2025
Часы
15.04.2025
Часы
5 минут
Глазик
62
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0

Вступление

В этой статье описывается процесс создания пагинатора используя Django и HTMX. А в качестве UI-библиотеки - daisyUI. Это будет простой и без изысков пагинатор, необходимый минимум для работы. Так же в этой статье не будет описываться процесс создания и настройки виртуального окружения, и установки всех сопутствующих пакетов и модулей.
Вот так, на примере моего нового сайта будут выглядеть кнопки пагинации
Так же, данная статья является лишь одной из трёх статей про создание собственного пагинатора на Django. В этой, я лишь создам базу, основу, на которой мы доведём данный пагинатор до логического завершения. Во второй, мы добавим фильтры и сортировку. В третьей, добавим поле поиска. Итак, приступим.

Пишем фронтенд

Я предпочитаю начинать свою работу с фронтенда, а там как пойдёт ┐(シ)┌. Для начала, нам потребуется страница для размещения пагинатора и шаблоны для отрисовки загружаемого контента. Например, у меня есть домашняя страница, мне нужно вставить и подключить следующий шаблон.
<div class="max-w-[1200px] w-[95%] mb-[50px]">
{% include 'parts/article-simple-paginator.html' with articles=articles %}
</div>
Не так важно во что ты обернёшь подключаемый шаблон. Я вот, собираюсь подгружать статьи и хочу чтобы они все были в столбик, но не слишком широкий. Важно то, что нужно передать переменную articles в подключаемый шаблон parts/article-simple-paginator.html. Откуда её взять, смотри в следующей главе, сейчас разберём другие составляющие наших шаблонов.
Исходный код parts/article-simple-paginator.html:
{% load static %}
{% load i18n %}

<div id="search-results" class="flex flex-col gap-[10px]">

{% include 'parts/article-cards.html' with articles=articles %}
{% include 'parts/paginator-buttons.html' %}

</div>
Данный сниппет кода представляет собой флекс-бокс в колонку, для карточек статей и собственно кнопок пагинатора. Когда мы подключаем шаблон для отрисовки карточек статей, мы так же не забываем отправить туда articles переменную. После чего подключаем и пагинатор.
Теперь, собственно говоря, про пагинатор и его кнопки, шаблон parts/paginator-buttons.html:
{% load i18n %}
{% load static %}

<form id="pagination_buttons" class="join self-center">
{% csrf_token %}
<input class="hidden" value="" name="one" type='text'>
<input class="hidden" value="2" name="two" type='text'>
<input class="hidden" value="3" name="three" type='text'>

{% if is_articles_prev %}
<button
class="join-item btn"
hx-post="?page=1"
hx-target="#search-results"
hx-replace-url="true"
value="1"
name="page"
>
{% trans '<<' %}
</button>
<button
class="join-item btn"
hx-post="?page={{articles_prev_page}}"
hx-target="#search-results"
hx-replace-url="true"
value="{{articles_prev_page}}"
name="page"
>
{% trans '<' %}
</button>
{% else %}
<button class="join-item btn btn-disabled">{% trans '<<' %}</button>
<button class="join-item btn btn-disabled">{% trans '<' %}</button>
{% endif %}

<button class="join-item btn btn-disabled">{{articles_start_page}}/{{articles_length}}</button>

{% if is_articles_next%}
<button
class="join-item btn"
hx-post="?page={{articles_next_page}}"
hx-target="#pagination_buttons"
hx-swap="outerHTML"
hx-replace-url="true"
value="{{articles_next_page}}"
name="page"
>
{% trans '>' %}
</button>
<button
class="join-item btn"
hx-post="?page={{articles_length}}"
hx-target="#search-results"
hx-replace-url="true"
value="{{articles_length}}"
name="page"
>
{% trans '>>' %}
</button>
{% else %}
<button class="join-item btn btn-disabled">{% trans '>' %}</button>
<button class="join-item btn btn-disabled">{% trans '>>' %}</button>
{% endif %}
</form>
Данный пагинатор реализован как форма с возможностью отправлять сразу несколько запросов. И отличаются эти запросы только номером следующей страницы. В самом начале я передаю csrf_token и несколько инпутов, они пока не нужны, но в следующей статье мы их дополним для реализации фильтрации и сортировки.
Отправка формы происходит при помощи одного из механизмов работы вставки параметров в запрос. В моём случае я воспользовался механизмом автоматической вставки параметров в запрос ближайшей формы в которую обёрнуты мои кнопки.
Он, пагинатор, состоит из 4-х кнопок и одной неактивной кнопки для показа текущей страницы пагинации и сколько ещё осталось. И вот как ведут себя эти кнопки:
  1. Кнопки >> и << отправляют на последнюю и первую страницу соответственно. Если пользователь уже на последней или первой странице, то эти кнопки отключаются.
  2. Кнопки > и < отправляют на следующую и предыдущую страницу соответственно. Если пользователь уже на последней или первой странице, то эти кнопки отключаются
Кнопки << < и >> имеют значение в атрибуте hx-target="#search-results", то есть корневой элемент отображения статей и пагинатора. При нажатии весь узел заменяется, а не дополняется новыми статьями так, как в случае с кнопкой >
На этом фронтенд часть завершена. Подводя итог, тебе нужны следующие шаблоны:
  1. parts/article-cards.html - для отрисовки карточек товаров
  2. parts/article-simple-paginator.html - комбинирует список карточек товаров и кнопки пагинации
  3. parts/paginator-buttons.html - интерфейс для управления пагинацией
  4. Место, куда можно было бы вставить parts/article-simple-paginator.html.

Пишем бэкенд

Перед тем как приступить к созданию бэкенда для пагинатора нужно обговорить несколько деталей. В частности, почему я не буду использовать REST API для пагинации.
Это решение, на самом деле, продиктовано необходимостью моего сайта иметь пагинатор на главной странице, а не на отдельном разделе сайта, как это сделано уже на этом сайте. Плюс, я хочу чтобы у людей была возможность сохранять в закладках необходимые им страницы пагинации.
Как ты мог заметить на всех кнопках пагинатора присутствует атрибут hx-replace-url. Он меняет старый URL на новый, по которому был отправлен запрос. Очень классная фича HTMX.
Вот содержимое views.py файла, где находится представление для домашней страницы.
from django.shortcuts import render
from django.http import Http404
from django.core.paginator import Paginator
from django.utils.translation import gettext as _
from django.utils.translation import get_language
from django.views.generic import TemplateView
from Backend.models import *



class HomeView(TemplateView):

template_name = 'domashnyaya.html'
page_size = 2

def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)

return context
def fetch(self, request, context, page_num, *args, **kwargs):
# Filter out other languages versions and order by time published, newest first
articles = Article.objects.filter(lang_type=get_language()).order_by('-time_published')
# Create a paginator, build-in Django one
paginator = Paginator(articles, self.page_size)
pagination_length = paginator.num_pages
# Check if page out of range
if page_num > pagination_length or page_num <= 0:
raise Http404()
# Paginate to page
page = paginator.page(page_num)
is_prev = page.has_previous()
is_next = page.has_next()
page_articles = page.object_list
# Update context
context.update({
'articles': page_articles,
'articles_start_page': page_num,
'articles_next_page': page_num + 1,
'articles_prev_page': page_num - 1,
'articles_length': pagination_length,
'is_articles_next': is_next,
'is_articles_prev': is_prev
})

def post(self, request, *args, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
page_num = int(request.POST.get('page', 1))
self.fetch(request, context, page_num, *args, **kwargs)
return render(request, 'parts/article-simple-paginator.html', context=context)

def get(self, request, *args, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
page_num = int(request.GET.get('page', 1))
self.fetch(request, context, page_num, *args, **kwargs)
return render(request, self.template_name, context=context)
Данное представление создано в виде класса ибо, это удобно. И работает оно с двумя запросами, POST и GET. Различие их в том, куда они отрисовывают страницы пагинации. Если при GET запросе отрисовывается целая страница, то при POST запросе только необходимая часть, уже ранее известный тебе parts/article-simple-paginator.html шаблон.
Метод fetch инкапсулирует общую логику между запросами GET и POST. В следующей статье мы расширим этот метод и добавим фильтрацию с сортировкой, ну а пока подключим данное представление в urls.py:
from django.urls import path
from .views import HomeView

urlpatterns = [
path('', HomeView.as_view(), name='domashnyaya'),
]

Заключение

Не так уж и сложно на самом деле. Если, конечно, сравнивать это с тем, что мне пришлось написать для работы пагинатора на этом сайте. А чтобы его написать, пришлось писать огромное полотно JS-кода, и ещё больше кода для работы непосредственно кнопок пагинации.
Ещё больше пришлось его переписывать ( ; ω ; )
Так же, динамическая замена URL очень сильно упростила жизнь. Реализовать это на JS не сложно, но есть вещи, которые просто не хочется реализовывать своими силами.
И вот так просто, можно реализовать пагинатор на Django используя HTMX.


Комментарии

(0)

captcha
Отправить
Сейчас тут пусто. Буть первым (o゚v゚)ノ

Другое

Похожие статьи


О дупликатах и неканонических страницах при имплементации пагинатора

Часы
02.03.2025
Глазик
65
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0
В этой статье я на примере своего сайта покажу, что такое дублированный(не канонический) контент появившийся в Google search console в результате внедрения пагинатора или бесконечной ленты. Со статистикой и графикой. …

Как сделать кнопку загрузки контента используя Django, REST API, HTMx

Часы
01.04.2025
Глазик
52
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0
В этой статье я опишу способ, как можно реализовать асинхронную загрузку контента(статей) при помощи кнопки "Больше", которая сделана при помощи Django, REST API, HTMx и стилизовано при помощи DaisyUI

Как сделать простой пагинатор на Django и HTMx. Добавляем сортировку и фильтры ч. 2

Часы
08.04.2025
Глазик
47
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0
В этой статье я опишу процесс и основные блоки кода, для того чтобы добавить сортировку и фильтрацию к пагинатору. Данный пагинатор написан на Django используя HTMx.

Как добавить форму обратной связи на Django, HTMx

Часы
11.04.2025
Глазик
36
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0
В этой статье, я опишу как добавить на ваш Django-сайт форму обратной связи используя HTMx и немного DaisyUI, в качестве UI-библиотеки. Всё будет сделано на примере моего нового сайта. Но …

Как кастомизировать 404 и 500 страницы ответов в Django

Часы
12.04.2025
Глазик
69
Сердечки
0
Соединённые точки
0
Соединённые точки
0
Соединённые точки
0
В этой статье я опишу процесс кастомизации таких страниц как 404 и 500. Я покажу два основных способа как это сделать и то как можно быстро и легко настроить сервер …

Использованные термины


Релевантные вопросы