Расширение веб-сокета htmx не отображает серверные сообщения

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

consumers.py:

from channels.generic.websocket import WebsocketConsumer
from django.template.loader import render_to_string
from myapp.models import Model1, Model2, Model3, Model4


class DashboardHeaderConsumer(WebsocketConsumer):
    def update_dashboard_counts(self):
        stats = [
            {'name': 'Model1', 'value': Model1.objects.count()},
            {'name': 'Model2', 'value': Model2.objects.count()},
            {'name': 'Model3', 'value': Model3.objects.count()},
            {'name': 'Model4', 'value': Model4.objects.count()},
        ]
        html = render_to_string('dashboard/header-stats.html', {'stats': stats})
        self.send(text_data=html)

    def connect(self):
        self.accept()
        self.update_dashboard_counts()

header-stats.html:

{% for stat in stats %}
<div class="col-sm-6 col-xl-3">
    <div class="dashboard-stat rounded d-flex align-items-center justify-content-between p-4">
        <div class="ms-3">
            <p class="mb-2">{{ stat.name }}</p>
            <h6 class="mb-0">{{ stat.value }}</h6>
        </div>
    </div>
</div>
{% endfor %}

my-template.html:

{% load static %}
{% block styles %}
    <link rel="stylesheet" href="{% static 'css/dashboard.css' %}">
{% endblock %}

<div class="container-fluid pt-4 px-4">
    <div class="row g-2 mb-2 stats-wrapper"
         hx-ext="ws"
         ws-connect="/ws/dashboard/header/"
         hx-target=".stats-wrapper"
         hx-swap="innerHTML"
    >
    </div>
    <div class="active-tasks-scroll-container">
        <div class="row flex-nowrap g-2">
        </div>
    </div>
</div>

Я ожидаю, что появятся результаты подсчета. Однако, несмотря на то, что сообщение получено и его можно увидеть в разделе "Запрос сетевого веб-сокета", dom пуст. Вот что отображается:

<html lang="en" data-bs-theme="dark">
<head>
    <meta charset="UTF-8">
    <title>App Title</title>
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/index.css" rel="stylesheet">
    <script src="/static/js/bootstrap.bundle.min.js"></script>
    <script src="/static/js/htmx.min.js"></script>
    <script src="/static/js/htmx-ext-ws%402.0.2"></script>

    <style>      .htmx-indicator {
        opacity: 0
    }

    .htmx-request .htmx-indicator {
        opacity: 1;
        transition: opacity 200ms ease-in;
    }

    .htmx-request.htmx-indicator {
        opacity: 1;
        transition: opacity 200ms ease-in;
    }      </style>
</head>
<body>
<div class="content-wrapper" id="content-wrapper">
    <div class="container-fluid pt-4 px-4">
        <div class="row g-2 mb-2 stats-wrapper" hx-ext="ws" ws-connect="/ws/dashboard/header/"
             hx-target=".stats-wrapper" hx-swap="innerHTML">
        </div>
        <div class="active-tasks-scroll-container">
            <div class="row flex-nowrap g-2">
            </div>
        </div>
    </div>
</div>
</body>
</html>

Я перепробовал все, начиная с отправки результата в формате json, добавления идентификатора, добавления hx-swap-oob="beforeend", установки hx-receive, ... ничего не работает.

Я думаю, вы отправляете необработанный HTML-код из Django Channels consumer self.send(text_data=html) но HTML-код не содержит .stats-wrapper div, поэтому HTMX понятия не имеет, какую часть DOM заменить.

Объект подкачки ищет .stats-wrapper, но это не включено в ответ.

Итак, чтобы исправить это, обновите свой header-stats.html шаблон следующим образом:


<div class="row g-2 mb-2 stats-wrapper">
    {% for stat in stats %}
    <div class="col-sm-6 col-xl-3">
        <div class="dashboard-stat rounded d-flex align-items-center justify-content-between p-4">
            <div class="ms-3">
                <p class="mb-2">{{ stat.name }}</p>
                <h6 class="mb-0">{{ stat.value }}</h6>
            </div>
        </div>
    </div>
    {% endfor %}
</div>

и если вы не хотите включать .stats-wrapper в свой ответ (а отправляете только внутреннее содержимое), то сделайте ответ внеполосным:

<div class="row g-2 mb-2 stats-wrapper" hx-swap-oob="true">
    {% for stat in stats %}
    ...
    {% endfor %}
</div>

Таким образом, HTMX найдет соответствующий .stats-wrapper в DOM и заменит его, даже если он не предназначен напрямую.

Вернуться на верх