3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер

3 горизонтальные линии, бургер
Удалить все
ЗАГРУЗКА ...

Содержание



    Как реализовать кнопку подгрузки дополнительных статей. На Django, REST API, HTMx и daisyUI

    Часы
    01.04.2025
    /
    Часы
    01.10.2025
    /
    Часы
    5 минут
    Глазик
    388
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0

    Вступление

    В этой статье я покажу как можно реализовать подгрузку дополнительного контента, аля страниц пагинации. С использованием django, rest_API, htmx и daisyUI. Так же я исхожу из мысли, что ты, мой дорогой уже создал виртуальное окружение для django-проекта, установил и настроил rest-api и в целом имеешь проект готовый для разработки. В этой статье настроек и предустановок не будет, сразу перейдём к сути. Демонстрировать всё я буду на своём новом сайте, про историю.

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

    Для начала, нам потребуется страница для размещения кнопки загрузки контента и шаблоны для отрисовки загружаемого контента. Например, у меня есть домашняя страница, мне нужно вставить подключить следующий шаблон.
    <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 %} {% load addstr %} <div id="search-results" class="flex flex-col gap-[10px]"> {% include 'parts/article-cards.html' with articles=articles %} {% if is_not_init %} {% if next %} {% include 'parts/more-button.html' with more_url=next %} {% endif %} {% else %} {% with "api/articles-cards/?page="|addstr:articles_start_page as next_url %} {% include 'parts/more-button.html' with more_url=next_url %} {% endwith %} {% endif %} </div>
    Данный сниппет кода представляет собой флекс-бокс в колонку, для карточек статей и собственно кнопки загрузки в самом конце. Когда мы подключаем шаблон для отрисовки карточек статей, мы так же не забываем отправить туда articles переменную.
    После (выделено жёлтым) идёт логика отображения кнопки подгрузки контента. Если is_not_init равно false, то есть, если это мой первый визит страницы, то при помощи django-тега with я вручную собираю ссылку которую потом, будет использовать кнопка для отправки запроса на сервер.
    Хочу заметить, что я ещё использую кастомный django-фильтр addstr, его нужно будет создать самостоятельно. У меня нет статьи о кастомных фильтрах, но можно подсмотреть реализацию такого фильтра на вот этой вот странице (смотри второй ответ!!!)
    Но если, это всё-таки не первый визит и is_not_init равно true, то использя rest_api пагинатор, я получаю и отравляю ссылку на следующую страницу. И если, переменная next не определена, то есть мы в конце и больше нечего загружать, мы не отрисовываем данную кнопку.
    Теперь, собственно говоря, про саму кнопку загрузки, шаблон parts/more-button.html:
    {% load i18n %} {% load static %} <button id="more_btn" class="btn btn-error btn-block" hx-get="{{more_url}}" hx-target="#more_btn" hx-indicator=".htmx-indicator" hx-swap="outerHTML"> {% trans 'Ещё' %} <span class="loading loading-bars loading-xl htmx-indicator"></span> </button>
    Пройдёмся по атрибутам, в порядке нисходящем).
    1. id - обычный АйДи кнопки, потребуется для того чтобы после можно было её найти
    2. class - используя классы от daisyUI, стилизуем кнопку
    3. hx-get - куда отправляем запрос
    4. hx-target - куда вставляем ответ от сервера
    5. hx-indicator - индикатор для отображения пока грузится контент
    6. hx-swap - как вставляем контент, относительно hx-target. outerHTML значит вставить загруженный контент в родительский элемент дерева DOM, с сохранением самой кнопки.
    Класс .htmx-indicator - это указатель со стилями по умолчанию. Собственно, указываем, что это наш индикатор.
    Можно ещё рассмотреть шаблон parts/article-cards.html, но в этом не очень много смысла, ибо он уже зависит от того что конкретно, ты хочешь отобразить. В моём случае это статьи, но в твоём это может быть что угодно. Но в любом случае, это обязательно должна быть коллекция чего-то. И вот код:
    {% load static %} {% load i18n %} {% for article in articles %} <div class="card card-side bg-base-100 shadow-sm flex flex-row flex-wrap rounded-none"> <figure class="max-w-[300px] min-w-[300px] min-h-[300px] m-[0]"> {% if article.preview %} <img class="flex items-center justify-center" src="{% get_media_prefix %}{{article.preview.file}}" alt="Album" /> {% else %} <img class="flex items-center justify-center" src="{% static 'media/images/default-article-preview.svg' %}" alt="Album" /> {% endif %} </figure> <div class="card-body basis-[300px]"> <h3 class="card-title">{{article.header}}</h2> <p>{{article.description}}</p> <div class="flex flex-row gap-[5px] flex-wrap"> {% for figure in article.figures.all %} <button class="btn btn-xs"> <img width="16" src="{% static 'media/images/person.svg' %}"/> {{figure.name}} </button> {% endfor %} </div> <div class="flex flex-row flex-wrap gap-[5px]"> {% for cat in article.categories.all %} <button class="btn btn-xs"> <img width="16" src="{% static 'media/images/category.svg' %}"/> {{cat.name}} </button> {% endfor %} {% for epoch in article.epoch.all %} <button class="btn btn-xs"> <img width="16" src="{% static 'media/images/epoch.svg' %}"/> {{epoch.name}} </button> {% endfor %} {% for period in article.period.all %} <button class="btn btn-xs"> <img width="16" src="{% static 'media/images/period.svg' %}"/> {{period.name}} </button> {% endfor %} </div> <div class="card-actions justify-end"> <button class="btn btn-error">{% trans 'Читать' %}</button> </div> </div> </div> {% endfor %}
    И вот, так это выглядит на самом сайте:
    Я ещё не совсем полностью стилизовал сайт, но уже не плохо, я думаю

    Пишем бэкенд

    Теперь про бэкенд. Начнём с того, что напишем класс-представление для той страницы, где надо подгружать статьи. В файле, Frontend/views.py:
    from django.shortcuts import render from django.utils.translation import gettext as _ from django.utils.translation import get_language from django.views.generic import TemplateView from Backend.models import * from Backend.views import StandardtPagination class HomeView(TemplateView): template_name = 'domashnyaya.html' def get_context_data(self, **kwargs): context = super(HomeView, self).get_context_data(**kwargs) return context def get(self, request, *args, **kwargs): context = super(HomeView, self).get_context_data(**kwargs) page_num = request.GET.get('page', 2) context.update({ 'articles': Article.objects.filter(lang_type=get_language()).order_by('-time_published')[:StandartPagination.page_size], 'articles_start_page': page_num }) return render(request, self.template_name, context=context)
    В общем и целом, идея такова: нужно вернуть коллекцию статей и номер страницы пагинации при GET запросе. Вообще, это всё не обязательно писать, если тебе не надо контент при первом запросе, то есть ты не против пустой страницы изначально.
    Здесь всё, хотя ещё надо заметить, как и сколько элементов я возвращаю. Фильтрую я их по языку (не обязательно если одноязычный сайт). Сортирую по дате публикации(сначала идут самые новые). И наконец, возвращаю ровно столько, сколько определено в пагинаторе StandartPagination.
    Теперь напишем сереализатор той модели, которую нужно будет подгружать. Это для работы с REST API. И, так как у меня это статьи, выглядит этот сереализатор вот так, в файле Backend/serializers.py:
    from rest_framework import serializers from .models import Article class ArticleSerializer(serializers.ModelSerializer): class Meta: model = Article fields = ('id', 'lang_en', 'lang_be', 'lang_ru', 'lang_type', 'is_audio_version', 'is_pdf_version', 'slug', 'title', 'description', 'meta_keywords', 'time_published', 'time_updated', 'header', 'content', 'preview', 'figures', 'categories', 'epoch', 'period', 'links', 'files', 'facts')
    Да, столько вот у меня полей для статьи, много ...
    С готовым серелиазатором и моделью для подгрузки, можно написать ещё один класс-представление, специально для подгрузки при нажатии кнопки "Ещё":
    from django.utils.translation import get_language from rest_framework import viewsets, generics from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.response import Response from rest_framework.pagination import PageNumberPagination from .serializers import ArticleSerializer from .models import Article class StandartPagination(PageNumberPagination): page_size = 2 page_size_query_param = 'page_size' max_page_size = len(Article.objects.all()) class ArticlePaginatedModelView(generics.ListAPIView): serializer_class = [ArticleSerializer] renderer_classes = [TemplateHTMLRenderer] template_name = 'parts/article-simple-paginator.html' pagination_class = StandartPagination queryset = Article.objects.all().order_by('-time_published') def list(self, request): queryset = self.get_queryset().filter(lang_type=get_language()) paginator = StandartPagination() page = paginator.paginate_queryset(queryset,request, ArticleModelView) next = paginator.get_next_link() return Response({ 'articles': page, 'is_not_init': True, 'next': next })
    Здесь, мы создаём пагинатор, унаследовавшись от класса PageNumberPaginator от REST API. Где указываем количество элементов на страницу(page_size) и всего доступных элементов(max_page_size).
    Непосредственно загрузкой необходимых статей будет заниматься класс ArticlePaginatedModelView, которую мы сделаем, унаследовавшись от миксина ListAPIView. Специальный класс от REST API, который создан, чтобы проходить по большим коллекциям элементов.
    Правда, его нужно предварительно настроить, чтобы он заработал корректно:
    1. serializer_class - Указываем серелиазатор, который будет испльзоваться для получения сырых данных о модели
    2. renderer_classes - то как будем возвращать результат, если не указывать будет возвращать JSON файл, нам нужен отрендереный кусок HTML, по этому используем TemplateHTMLRenderer
    3. template_name - в какой шаблон будет отрисовываться коллекция подгружаемых элементов
    4. pagination_class - как будем проходить по коллекции элементов
    5. queryset - какие элементы будем подгружать
    И чтобы вернуть, конкретно коллекцию, необходимо переопределить метод list. Где мы фильтруем нашу коллекцию по языку(необязательно). Можно просто get_queryset().
    В документации при переопределении метода list, рекомендуется использовать именно get_queryset метод, но не queryset поле класса. Это связано с кешем и скоростью возврата ответа.
    Дальше, получаем срез необходимых элементов для подгрузки и ссылку на следующую страницу, если таковая будет (элементы закончились). И чтобы обозначить, что данный запрос это не первый, определяем тут переменную is_not_init = True.
    Всё готово, осталось только подключить данные представления в Backend/urls.py:
    from django.urls import path, include from rest_framework import routers from Backend import views router = routers.DefaultRouter() router.register(r'articles', views.ArticleModelView, 'article') urlpatterns = [ path('api/', include(router.urls)), path('api/articles-cards/', views.ArticlePaginatedModelView.as_view(), name='articles-cards') ]

    Заключение

    Данного кода должно хватить, чтобы написать обычную кнопку подгрузки контента с сервера, используя только Django как основу, немного REST API и ещё меньше HTMx.


    Не забудь поделиться, лайкнуть и оставить комментарий)

    Комментарии

    (0)

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

    Другое

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


    Как построить API для сайта используя Django REST API

    Часы
    24.02.2025
    /
    Часы
    02.10.2025
    Глазик
    4259
    Сердечки
    0
    Соединённые точки
    1
    Соединённые точки
    6
    Соединённые точки
    0
    Здесь ты узнаешь как написать свой API используя Django REST API. Добавляется он для того, чтобы построить API для доступа со стороны клиента (фронтенда). Так же демонстрирую как данный API …

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

    Часы
    02.04.2025
    /
    Часы
    01.10.2025
    Глазик
    443
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    В этой статье я опишу то, как создать пагинатор используя Django и HTMx библиотеку. И то, почему это было так просто в сравнении с пагинатором на моём сайте. С шаблонами …

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

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

    Как добавить свою 404, 500 страницу в Django

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