【问题标题】:How can I make SSE with Python (Django)?如何使用 Python (Django) 制作 SSE?
【发布时间】:2019-06-17 00:00:34
【问题描述】:

我有两个不同的页面,一个 (A) 显示从模型对象获取的数据,另一个 (B) 更改其字段。 我希望当发布数据从 B 发送到服务器时,服务器会更改 A 中的值。 最好的方法是什么?

这个例子对我有用,但它是用 PHP 编写的……有办法用 Python 复制它吗? https://www.w3schools.com/html/html5_serversentevents.asp

【问题讨论】:

标签: python django django-models server-sent-events


【解决方案1】:

这是来自 Django 中 w3schools 的工作示例:

模板

<!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')

网址

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')

以及在Notification 模型中创建条目的send_notification 函数(只需从代码中的任何位置调用此函数):

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

就是这样,就这么简单。

【讨论】:

  • 它有点工作,但行为不是我所期望的。页面会随着新的服务器时间不断更新。如果我想向我的客户发送特定(仅一次)事件怎么办?
  • 嗨@Ivan 感谢您的更新。在了解 SSE 无法按预期与 Django 一起工作后,我发布了我的答案,因为它不支持持久连接:(
  • 值得一提的是StreamingHttpResponse很贵,应该避免使用。
  • 既然 Django 3.1 支持异步视图,这些关于性能和持久连接的 cmets 是否仍然相关? documentation 表示“主要好处是能够在不使用 Python 线程的情况下为数百个连接提供服务。这允许您使用慢速流式传输、长轮询和其他令人兴奋的响应类型”
【解决方案2】:

看完this,我想我明白了整个事情(如果我错了请评论)。

Django 本身并不支持保持连接。这意味着,当客户端从服务器获取消息时,连接会立即关闭(就像任何经典的 HTTP 请求/响应循环一样)。

text/event-stream 请求的不同之处在于,客户端每秒自动尝试重新连接到服务器(长度可以通过retry 参数更改)。

不幸的是,在这种情况下使用 SSE 似乎没有兴趣,因为它与轮询具有相同的缺点(即每 X 秒发生一个请求/响应周期)。

正如预期和其他答案中提到的那样,我需要 django-channels 创建一个持久连接,以防止 HTTP 请求/响应开销并确保立即发送消息。

【讨论】:

    【解决方案3】:

    正如其他答案中提到的,您将需要使用 Django Channels 来正确处理异步通信而不占用线程。

    例如,请参阅the django-eventstream library,它使用 Channels 来实现 SSE。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-01
      • 2018-04-19
      • 2020-08-05
      • 1970-01-01
      • 1970-01-01
      • 2023-01-18
      相关资源
      最近更新 更多