Пагинация с бесконечной прокруткой в Django с помощью HTMX
Несколько лет назад я сделал веб-приложение на Django. Я также добавил в него пагинацию. Но чего-то не хватало...
Да, это была бесконечная пагинация прокрутки!
Итак, в сегодняшнем посте мы будем создавать пагинацию с бесконечной прокруткой в Django, используя HTMX.
Кроме того, пагинатор будет работать, даже если у пользователя отключен Javascript.
Для начала создадим проект django и приложение django
- Установите django
python -m pip install django
- Создайте приложения и проекта django
django-admin startproject InfiniteScroll
cd ./InfiniteScroll
python manage.py startapp core
- Добавьте наше приложение django в
INSTALLED_APP
вInfiniteScroll/setting.py
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Our Apps
'core.apps.CoreConfig' #<-- ADD THIS
]
- Чтобы облегчить некоторые задачи, создадим в нашем основном приложении файл утилиты с именем
utils.py
.
from django.core.paginator import Paginator
def is_htmx(request, boost_check=True):
hx_boost = request.headers.get("Hx-Boosted")
hx_request = request.headers.get("Hx-Request")
if boost_check and hx_boost:
return False
elif boost_check and not hx_boost and hx_request:
return True
def paginate(request, qs, limit=2):
paginated_qs = Paginator(qs, limit)
page_no = request.GET.get("page")
return paginated_qs.get_page(page_no)
Функция is_htmx
проверяет, является ли запрос htmx-запросом или нет. Также проверяется, имеет ли запрос статус "boosted".
Функция paginate
поможет нам разбить набор запросов на страницы. При желании можно настроить параметр ограничения. Для тестирования я установил ограничение на 2
.
- В файл
views.py
добавьте следующее:
from django.shortcuts import render
from django.contrib.auth.models import User
from .utils import is_htmx, paginate
# Create your views here.
def index(request):
users = paginate(request, User.objects.all())
if is_htmx(request):
return render(request, "islands/user_list.html", {"users": users})
return render(request, "index.html", {"users": users})
Здесь мы возвращаем список пользователей, а если запрос является htmx-запросом, то мы возвращаем его части.
- Давайте создадим шаблоны:
templates/islands/pagination.html
<div {% if page.has_next %} hx-get="?page={{ page.next_page_number }}" hx-trigger="revealed"
hx-swap="outerHTML" {% endif %}>
{% if page.has_next or page.has_previous %}
<nav class="pagination is-centered is-small mt-2" role="navigation" aria-label="pagination">
<ul class="pagination-list">
{% if page.has_previous %}
<a class="pagination-link" aria-label="Goto page 1" href="?page=1">First Page</a>
<a class="pagination-link" href="?page={{ page.previous_page_number }}">«
Previous</a>
{% endif %}
<a class="pagination-link is-current" aria-label="Goto page {{ page.number }}">{{ page.number }}</a>
{% if page.has_next %}
<a class="pagination-link" href="?page={{ page.next_page_number }}">Next page
»</a>
<a class="pagination-link" aria-label="Goto page 4&query="
href="?page={{ page.paginator.num_pages }}">Last
Page</a>
{% endif %}
</ul>
</nav>
{% endif %}
<script>
htmx.on("htmx:load", function (evt) {
document.querySelector(".pagination").style.display = "none";
});
</script>
</div>
Элемент .pagination
имеет следующие атрибуты:
hx-get
: Ссылка на следующую страницуhx-trigger
: Триггерное событие, которое будет вызывать конечную точкуhx-swap
: Тип подмены, которая должна произойти, здесь это outerHTML
Этот код довольно прост.
Сначала мы проверяем, есть ли следующая страница или нет, используя django, затем, если страница есть, мы вызываем функцию ?page.next_page_number }}
, например, если текущая страница равна 1
, то {{ page.next_page_number }}
вернет 2
и т.д.
Далее указывается тип события-триггера.
Он означает, какие события должны произойти, чтобы вызвать запрос от htmx. В данном случае событие revealed
означает, что когда элемент пагинации будет раскрыт, htmx выполнит запрос. Подробнее здесь.
И, наконец, у нас есть hx-swap
, то есть, когда htmx получит ответ, нам нужно заменить весь html в .pagination
на ответный html. Подробнее тут.
Здесь мы скрываем .pagination
при каждом htmx-запросе. Если js отключен, то пользователь получит кнопки пагинации. Это называется прогрессивным расширением. templates/islands/user_list.html
.
{% for user in users %}
<div class="box">
<p class="subtitle is-1">{{ user }}</p>
{% if user.is_staff %}
<span class="tag is-success">Staff</span>
{% endif %}
</div>
<br>
{% endfor %}
{% include 'islands/pagination.html' with page=users %}
Это простой файл шаблона django, который будет принимать список пользователей и показывать их с помощью цикла for-loop
.
У нас также есть наш шаблон пагинации, который будет отображаться после списка пользователей. templates/index.html
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Infinite Scroll Pagination</title>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/bulma-prefers-dark" />
</head>
<body>
<div class="container">
<section class="section">
{% include 'islands/user_list.html' %}
</section>
</div>
</body>
</html>
Здесь мы используем bulma и bulma-prefers-dark для стилизации нашей страницы.
Как видите, мы можем использовать наши частичные шаблоны в любом месте нашего кода, поэтому мы можем просто включить islands/user_list.html
в index.html
.
- Теперь, наконец, подключим его в
urls.py
.InfiniteScroll/urls.py
.
from django.contrib import admin
from django.urls import path
from core.views import index #<-- ADD THIS
urlpatterns = [
path('admin/', admin.site.urls),
path('', index), #<-- ADD THIS
]
Импортируйте представление index
из core.views
и отобразите его в urlpatterns
- Теперь создадим суперпользователя для входа в панель администратора
python manage.py createsuperuser
Запустите сервер и перейдите на http://localhost:8000/admin/auth/user/, чтобы добавить еще несколько пользователей, а затем перейдите на индексную страницу для проверки.
python manage.py runserver
Демонстрация
Ну вот и всё! Спасибо за прочтение!