Как создать адаптивную таблицу с помощью HTMX и Django

Как создать адаптивную таблицу с помощью HTMX и Django

30 марта 2022 г.

Как разработчик, вам нужно будет создавать таблицы в своих веб-приложениях. Чтобы создать таблицу в Django, вы обычно реализуете набор API-интерфейсов на стороне сервера, которые передают данные клиенту, и используете библиотеку таблиц Javascript на стороне клиента. Но вы также можете реализовать HTML-таблицу. Недостатком этого подхода является то, что каждое действие, такое как сортировка или поиск, будет обновлять всю страницу. И это может быть неприятным опытом для пользователя. Но знаете ли вы, что есть лучший способ создавать таблицы? Эта статья покажет вам, как использовать Django и htmx для разработки функциональных и адаптивных таблиц.


Эта статья покажет вам, как создать таблицу с помощью django-tables2 и htmx. Таблица, которую я собираюсь реализовать, выглядит так:


Набросок таблицы


Я предполагаю, что вы уже знакомы с Django и можете настроить проект Django самостоятельно. Поэтому я не буду проходить настройку проекта Django с нуля. Давайте начнем.


Пакеты, используемые для этого проекта


  1. [htmx] (https://htmx.org/). htmx — это небольшой Javascript, который поможет вам избежать Javascript. Усталость от Javascript реальна. Существует слишком много интерфейсных фреймворков, инструментов и опций Javascript. Интеграция некоторых из этих фреймворков в проект Django приводит к большой сложности. Вам нужно будет переключить контекст между Python и Javascript. Вам также необходимо понимать широкий набор инструментов, таких как node, npm, webpack и т. д. htmx позволяет разработчику Django придерживаться того, в чем Django хорош: на стороне сервера.

  1. [django-tables2] (https://github.com/jieter/django-tables2). Вы можете реализовать таблицу HTML, используя только Django, но есть способ легко создавать таблицы и управлять ими: django-tables2. Это приложение Django позволяет вам определять таблицы так же, как вы определяете модели Django. Он может автоматически генерировать таблицу на основе модели Django. Он поддерживает разбивку на страницы, сортировку таблиц на основе столбцов, пользовательские функции столбцов с помощью подклассов и многие другие функции.

  1. [фильтр джанго] (https://github.com/carltongibson/фильтр джанго). Я использую этот пакет для функции поиска. Он имеет API, похожий на ModelForm Django, и хорошо работает с django-tables2.

  1. [django-htmx] (https://github.com/adamchainz/django-htmx). Чтобы htmx работал, view Django должен иметь возможность определить, какой запрос сделан с использованием htmx, а какой нет. Вы можете создать свое промежуточное ПО или класс, который ваше представление может наследовать, чтобы справиться с этим. Но для этого проекта я использую django-htmx, потому что зачем изобретать велосипед. Он имеет промежуточное программное обеспечение, которое добавляет атрибут htmx к объекту request.

Как работает HTML


Прежде чем двигаться дальше, давайте поговорим о том, как работает htmx. Библиотека htmx дает вам доступ к AJAX. В ванильном HTML только <a> и <form> могут делать HTTP-запросы, и только события click и submit могут вызывать их. Кроме того, HTTP-запросы всегда заменяют весь экран. Но библиотека htmx преодолела все эти ограничения. Вы можете инициировать HTTP-запросы к любому элементу, который хотите. И это просто сделать, например (взято прямо с страницы htmx):





<кнопка hx-post="/clicked" hx-swap="outerHTML">


Нажми на меня



Когда вы нажимаете на кнопку, htmx выдает запрос AJAX к /clicked и заменяет всю кнопку HTML-ответом.


Я могу использовать ту же механику для стола. Все, что мне нужно сделать:


  1. Рендер всей страницы с первой страницей таблицы

  1. Когда вы запускаете какое-либо действие (сортировка, поиск, переход на определенную страницу), оно отправляет запрос AJAX на сервер.

  1. Сервер возвращает HTML-ответ (частичный HTML, только данные таблицы)

  1. Содержимое назначенного раздела заменяется новым HTML-ответом.

Для этого проекта я использую несколько атрибутов htmx, а именно:


  1. hx-получить. Чтобы отправить запрос GET на заданный URL.

  1. hx-триггер. Способ запуска запроса.

  1. hx-цель. Чтобы загрузить результат в целевой элемент.

  1. hx-своп. Чтобы поменять местами HTML, возвращенный в метод DOM.

  1. hx-индикатор. Сообщите пользователю, что что-то происходит, так как браузер не будет давать ему никакой обратной связи.

Модель


Во-первых, давайте создадим простую таблицу продуктов. Я сделаю стол из следующей модели.


продукты/models.py


из моделей импорта django.db


класс Продукт (модели.Модель):


Статус класса (модели.IntegerChoices):


АКТИВНЫЙ = 1, "Активный"


НЕАКТИВНО = 2, "Неактивно"


АРХИВИРОВАНО = 3, "Архивировано"


имя = модели.CharField(max_length=255)


категория = модели.CharField(max_length=255)


цена = модели.DecimalField(max_digits=10, decimal_places=2)


стоимость = модели.DecimalField (max_digits = 10, decimal_places = 2)


статус = модели.PositiveSmallIntegerField(choices=Status.choices)


защита str(я):


вернуть себя.имя


Таблица


Теперь я определю таблицу и укажу шаблон. Это переопределение шаблона необходимо, потому что мне нужно добавить некоторые атрибуты htmx к некоторым элементам. Мое определение таблицы выглядит следующим образом.


продукты/tables.py


импортировать django_tables2 как таблицы


from products.models импорт Продукт


класс ProductHTMxTable (таблицы. Таблица):


Мета класса:


модель = продукт


имя_шаблона = "таблицы/bootstrap_htmx.html"


Внутри шаблона bootstrap_htmx.html я расширяю исходный шаблон bootstrap4.html. Части, которые я расширяю, — это заголовки таблиц (используемые для сортировки) и часть разбиения на страницы. Обратите внимание, что hx-target указывает на div.table-container. Это связано с тем, что шаблон bootstrap4.html имеет контейнер div, который обертывает таблицу, а класс для контейнера – table-container. Htmx заменит данные div.table-container ответом сервера.


{# шаблоны/таблицы/bootstrap_htmx.html #}


{% расширяет "django_tables2/bootstrap4.html" %}


{% загрузить django_tables2 %}


{% загрузить i18n%}


{% блокировка table.thead%}


{% если table.show_header %}




{% для столбца в table.columns %}


<th {{ столбец.attrs.th.as_html }}


hx-get="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}"


hx-триггер = "щелчок"


hx-target="div.table-контейнер"


hx-своп = "внешний HTML"


hx-индикатор = ".прогресс"


стиль="курсор: указатель;">


{{столбец.header}}



{% конец для %}




{% конец%}


{% endblock table.thead %}


{# Блок пагинации переопределяет #}


{% блокировать разбивку на страницы.предыдущая%}



  • <div hx-get="{% querystring table.prefixed_page_field=table.page.previous_page_number %}"


    hx-триггер = "щелчок"


    hx-target="div.table-контейнер"


    hx-своп = "внешний HTML"


    hx-индикатор = ".прогресс"


    класс = "ссылка на страницу">



    {% транс 'предыдущий' %}




  • {% endblock pagination.previous %}


    {% блокировать pagination.range %}


    {% для p в table.page|table_page_range:table.paginator %}



  • <div class="ссылка-страница"


    {% if p != '...' %}hx-get="{% querystring table.prefixed_page_field=p %}"{% endif %}


    hx-триггер = "щелчок"


    hx-target="div.table-контейнер"


    hx-своп = "внешний HTML"


    hx-индикатор=".прогресс">


    {{ п }}




  • {% конец для %}


    {% endblock pagination.range %}


    {% блокировать разбиение на страницы.следующий%}



  • <div hx-get="{% querystring table.prefixed_page_field=table.page.next_page_number %}"


    hx-триггер = "щелчок"


    hx-target="div.table-контейнер"


    hx-своп = "внешний HTML"


    hx-индикатор = ".прогресс"


    класс = "ссылка на страницу">


    {% транс 'следующий' %}





  • {% endblock pagination.next %}


    Фильтр


    Для добавления фильтрации и поиска в таблице django-tables2 я буду использовать django-filter. Ниже приводится определение фильтра.


    продукты/filters.py


    из десятичного импорта Decimal


    из django.db.models импорт Q


    импортировать django_filters


    from products.models импорт Продукт


    класс ProductFilter (django_filters.FilterSet):


    запрос = django_filters.CharFilter (метод = 'universal_search',


    метка = "")


    Мета класса:


    модель = продукт


    поля = ['запрос']


    def universal_search (я, набор запросов, имя, значение):


    если значение.заменить(".", "", 1).isdigit():


    значение = десятичное (значение)


    вернуть Product.objects.filter(


    Q(цена=стоимость) | Q (стоимость = стоимость)


    вернуть Product.objects.filter(


    Q(имя__значок=значение) | Q(category__icontains=значение)


    Поскольку для всей таблицы есть только одна форма поиска, я сначала проверяю, являются ли входные данные цифрой. Если это так, ищите только столбцы «цена» и «стоимость». В противном случае выполните поиск в столбцах «имя» и «категория».


    Вид


    Представление будет отправлять полную страницу, если запрос сделан не с помощью htmx, и отправлять частичные результаты, когда запрос сделан с помощью htmx.


    продукты/views.py


    из django_tables2 импортировать SingleTableMixin


    из django_filters.views импортировать FilterView


    from products.models импорт Продукт


    из products.tables импортировать ProductHTMxTable


    из products.filters импортировать ProductFilter


    класс ProductHTMxTableView (SingleTableMixin, FilterView):


    table_class = ProductHTMxTable


    набор запросов = Продукт.объекты.все()


    filterset_class = фильтр продукта


    paginate_by = 15


    деф get_template_names (я):


    если self.request.htmx:


    template_name = "product_table_partial.html"


    еще:


    template_name = "product_table_htmx.html"


    вернуть имя_шаблона


    Шаблон для product_table_htmx.html будет отображать всю страницу вместе с заголовком HTML. Тег шаблона render_table сгенерирует таблицу в соответствии с нашим шаблоном tables/bootstrap_htmx.html.


    {# продукт/шаблоны/product_table_htmx.html #}


    {% расширяет "base.html" %}


    {% загрузить render_table из django_tables2 %}


    {% загрузить i18n%}


    {% загрузить crispy_forms_tags %}


    {% основной блок %}


    Таблица товаров


    {# Форма поиска #}


    <form hx-get="{% url 'product_htmx' %}"


    hx-target="div.table-контейнер"


    hx-своп = "внешний HTML"


    hx-индикатор = ".прогресс"


    класс = "встроенная форма">


    {% хрустящий filter.form %}



    {# Индикатор прогресса #}





    {# Фактическая таблица #}


    {% таблица render_table %}


    {% конечный блок%}


    Шаблон для product_table_partial.html будет отображать только часть таблицы. Он вернет только часть контейнера таблицы и саму таблицу. Обратите внимание, что мы не расширяемся от base.html.


    {# product/templates/product_table_partial.html #}


    {% загрузить render_table из django_tables2 %}


    {% таблица render_table %}


    Стиль


    Таблица, которую мы определяем, уже использует стиль таблицы Bootstrap 4. Но мне нужно добавить стиль, чтобы я мог видеть стрелку справа от столбца, когда я выполняю сортировку по столбцу. Стрелка будет указывать вверх или вниз в зависимости от сортировки по возрастанию или по убыванию. Также мне нужно указать ширину столбца, иначе столбец изменит размер при сортировке или поиске. После этого мне нужно только добавить стиль для индикатора прогресса.


    / Стиль таблицы /


    .table-контейнер th.asc: после {


    содержимое: '\0000a0\0025b2';


    поплавок: справа;


    .table-container th.desc:после {


    содержимое: '\0000a0\0025bc';


    поплавок: справа;


    .table-container таблица td:nth-child(1) {


    ширина: 5%;


    .table-container таблица td:nth-child(2) {


    ширина: 20%;


    .table-container таблица td:nth-child(3) {


    ширина: 50%;


    / Индикатор /


    .прогресс {


    высота: 4 пикселя;


    ширина: 100%;


    радиус границы: 2px;


    фоновый клип: padding-box;


    переполнение: скрыто;


    положение: родственник;


    .прогресс {


    непрозрачность: 0;


    .htmx-запрос .progress {


    непрозрачность: 1;


    .htmx-запрос.прогресс {


    непрозрачность: 1;


    .прогресс .неопределенный {


    цвет фона: синий;


    .прогресс .неопределенный: до {


    содержание: '';


    положение: абсолютное;


    фоновый цвет: наследовать;


    сверху: 0;


    слева: 0;


    внизу: 0;


    будет-изменение: влево, вправо;


    -webkit-анимация: неопределенный 2.1s кубический Безье (0,65, 0,815, 0,735, 0,395) бесконечный;


    анимация: неопределенный 2,1 с кубический-безье (0,65, 0,815, 0,735, 0,395) бесконечный;


    .прогресс .неопределенный: после {


    содержание: '';


    положение: абсолютное;


    фоновый цвет: наследовать;


    сверху: 0;


    слева: 0;


    внизу: 0;


    будет-изменение: влево, вправо;


    -webkit-анимация: неопределенный-короткий 2,1 с кубический-безье (0,165, 0,84, 0,44, 1) бесконечный;


    анимация: неопределенная-короткая 2,1 с кубическая-безье (0,165, 0,84, 0,44, 1) бесконечная;


    -webkit-анимация-задержка: 1,15 с;


    анимация-задержка: 1,15 с;


    @ключевые кадры неопределенные {


    0% {


    слева: -35%;


    справа: 100%;


    60% {


    слева: 100%;


    справа: -90%;


    100% {


    слева: 100%;


    справа: -90%;


    @ключевые кадры неопределенно-короткие {


    0% {


    слева: -200%;


    справа: 100%;


    60% {


    слева: 107%;


    справа: -8%;


    100% {


    слева: 107%;


    справа: -8%;


    Результат


    Итак, теперь у меня есть рабочий стол, который выглядит так.


    Сортировка


    Сортировка


    Пагинация


    Разбивка на страницы


    Поиск


    Поиск


    Резюме


    В этой статье я показал, почему вы должны сделать шаг назад в разработке внешнего интерфейса и тщательно взвесить все за и против внедрения среды Javascript в проект Django. Я показал вам, как использовать htmx с django-tables, чтобы создать отзывчивую и функциональную таблицу, чтобы вы могли сохранить легкость и компактность внешнего интерфейса.


    Также опубликовано здесь



    Оригинал
    PREVIOUS ARTICLE
    NEXT ARTICLE