Как я могу сделать SSE с помощью Python (Django)?

У меня есть две разные страницы: одна (A) отображает данные, взятые из объекта модели, а другая (B) изменяет свои поля. Я хотел бы, чтобы при отправке почтовых данных с B на сервер сервер менял значения в A. Как лучше всего это сделать?

Этот пример мог бы работать для меня, но он на PHP... есть ли способ воспроизвести его с Python? https://www.w3schools.com/html/html5_serversentevents.asp

Проверено это?

dirkgroten 23.01.2019 12:49
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
13
1
15 387
3

Ответы 3

Это рабочий пример из w3schools в Django:

шаблон

<!DOCTYPE html>
<html>
<body>

<h1>Getting server updates</h1>
<div id = "result"></div>

<script>
if (typeof(EventSource) !== "undefined") {
  var source = new EventSource("stream/");
  source.onmessage = function(event) {
    document.getElementById("result").innerHTML += event.data + "<br>";
  };
} else {
  document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>

</body>
</html>

взгляды

import datetime
import time
from django.http import StreamingHttpResponse

def stream(request):
    def event_stream():
        while True:
            time.sleep(3)
            yield 'data: The server time is: %s\n\n' % datetime.datetime.now()
    return StreamingHttpResponse(event_stream(), content_type='text/event-stream')

URL-адреса

urlpatterns = [
    path('stream/', views.stream, name='stream')
]

Обновлять:

Если вы хотите управлять своими уведомлениями, вы можете создать модель, например:

from django.db import models

class Notification(models.Model):
    text = models.CharField(max_length=200)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    sent = models.BooleanField(default=False)

Затем создайте представление, которое ищет первое неотправленное уведомление и отправляет его:

@login_required
def stream(request):
    def event_stream():
        while True:
            time.sleep(3)
            notification = Notification.objects.filter(
                sent=False, user=request.user
            ).first()

            text = ''

            if notification:
                text = notification.text
                notification.sent = True
                notification.save()

            yield 'data: %s\n\n' % text

    return StreamingHttpResponse(event_stream(), content_type='text/event-stream')

И функция send_notification, создающая запись в модели Notification (просто вызовите эту функцию из любого места вашего кода):

def send_notification(user, text):
    Notification.objects.create(
        user=user, text=text
    )

Вот так просто.

Это вроде работает, но поведение не то, что я ожидал. Страница постоянно обновляется с новым серверным временем. Что делать, если я хочу отправить определенное (только один раз) событие моему клиенту?

David D. 14.06.2019 17:45

Привет @Ivan спасибо за ваше обновление. Я опубликовал свой ответ после того, как понял, что SSE не будет работать должным образом с Django, потому что он не поддерживает постоянное соединение :(

David D. 18.06.2019 02:40

Стоит отметить, что StreamingHttpResponse дорог и его следует избегать.

adnanmuttaleb 10.06.2020 10:20

Актуальны ли эти комментарии о производительности и постоянных соединениях сейчас, когда Django 3.1 поддерживает асинхронные представления? В документация говорится: «Основные преимущества — это возможность обслуживать сотни подключений без использования потоков Python. Это позволяет использовать медленную потоковую передачу, длительный опрос и другие захватывающие типы ответов».

Benoit Blanchon 08.08.2020 15:17

Прочитав это, я думаю, что все понял (пожалуйста, прокомментируйте, если я ошибаюсь).

Django изначально НЕ поддерживает поддерживающие соединения. Это означает, что когда клиент получает сообщение от сервера, соединение немедленно закрывается (как и любой классический цикл запроса/ответа HTTP).

Что отличается от запроса text/event-stream, так это то, что клиент автоматически пытается переподключиться к серверу каждую секунду (длину можно изменить с помощью параметра retry).

К сожалению, кажется, что использование SSE в этом случае не представляет интереса, поскольку у него те же недостатки, что и у опроса (т. е. цикл запрос/ответ происходит каждые X секунд).

Как и ожидалось и упоминалось в других ответах, мне понадобятся django-каналы для создания постоянное соединение, которые предотвращают накладные расходы HTTP-запроса/ответа и обеспечивают немедленную отправку сообщения.

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

В качестве примера см. библиотека django-eventstream, который использует каналы для реализации SSE.

Другие вопросы по теме