实时动态更新Django模板

时间:2019-07-06 11:27:43

标签: python django ajax django-templates django-views

我正在构建一个django应用程序,它将提供实时数据。我是Django的新手,现在我专注于如何实时更新数据,而不必重新加载整个页面。

一些澄清:实时数据应该定期更新,而不仅仅是通过用户输入。

查看

def home(request):

    symbol = "BTCUSDT"
    tst = client.get_ticker(symbol=symbol)

    test = tst['lastPrice']

    context={"test":test}

    return render(request,
                  "main/home.html", context
                  )

模板

<h3> var: {{test}} </h3>

我已经问过这个问题,但是我有一些疑问:

有人告诉我要使用Ajax,这没关系,但是Ajax是否适合这种情况,我将在每个页面上每x秒加载一次实时更新的数据?

我还被告知要使用DRF(Django Rest Framework)。我一直在仔细研究它,但是我不清楚它在这种特殊情况下如何工作。

2 个答案:

答案 0 :(得分:1)

在下面,我在前面的评论中建议了一个清单,以实现基于Websocket和Django Channels的解决方案所需的操作清单。 最后给出了动机。

1)连接到Websocket并准备接收消息

在客户端上,您需要执行以下javascript代码:

<script language="javascript">
    var ws_url = 'ws://' + window.location.host + '/ws/ticks/';
    var ticksSocket = new WebSocket(ws_url);

    ticksSocket.onmessage = function(event) {
        var data = JSON.parse(event.data);
        console.log('data', data);
        // do whatever required with received data ...
    };
</script>

在这里,我们打开Websocket,然后在onmessage回调中详细说明服务器发送的通知。

可能的改进:

  • 支持SSL连接
  • 使用ReconnectingWebSocket:WebSocket API上的一个小型包装,可自动重新连接
    <script language="javascript">
        var prefix = (window.location.protocol == 'https:') ? 'wss://' : 'ws://';
        var ws_url = prefix + window.location.host + '/ws/ticks/';
        var ticksSocket = new ReconnectingWebSocket(ws_url);
        ...
    </script>

2)安装和配置Django通道和通道层

要配置Django频道,请遵循以下说明:

https://channels.readthedocs.io/en/latest/installation.html

Channel Layers是Django Channels的可选组件,它提供了“组”抽象,我们将在以后使用。您可以按照此处给出的说明进行操作:

https://channels.readthedocs.io/en/latest/topics/channel_layers.html#

3)发布Websocket端点

路由为Websocket(和其他协议)提供了已发布的终结点与关联的服务器端代码之间的映射,就像urlpattens在传统Django项目中为HTTP所做的一样

文件routing.py

from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from . import consumers

application = ProtocolTypeRouter({
    "websocket": URLRouter([
        path("ws/ticks/", consumers.TicksSyncConsumer),
    ]),
})

4)写出消费者

Consumer是一个类,为Websocket标准(以及可能的自定义)事件提供处理程序。从某种意义上说,它对Websocket的作用就像Django视图对HTTP的作用。

在我们的情况下:

  • websocket_connect():我们接受连接并将注册的客户端注册到“ ticks”组
  • websocket_disconnect():通过从组中删除che客户端进行清理
  • new_ticks():我们的自定义处理程序,它将接收到的滴答广播到其Websocket客户端
  • 我认为TICKS_GROUP_NAME是项目设置中定义的常量字符串值

文件consumers.py

from django.conf import settings
from asgiref.sync import async_to_sync
from channels.consumer import SyncConsumer

class TicksSyncConsumer(SyncConsumer):

    def websocket_connect(self, event):
        self.send({
            'type': 'websocket.accept'
        })

        # Join ticks group
        async_to_sync(self.channel_layer.group_add)(
            settings.TICKS_GROUP_NAME,
            self.channel_name
        )

    def websocket_disconnect(self, event):
        # Leave ticks group
        async_to_sync(self.channel_layer.group_discard)(
            settings.TICKS_GROUP_NAME,
            self.channel_name
        )

    def new_ticks(self, event):
        self.send({
            'type': 'websocket.send',
            'text': event['content'],
        })

5)最后:广播新的滴答声

例如:

ticks = [
    {'symbol': 'BTCUSDT', 'lastPrice': 1234, ...},
    ...
]
broadcast_ticks(ticks)

其中:

import json
from asgiref.sync import async_to_sync
import channels.layers

def broadcast_ticks(ticks):
    channel_layer = channels.layers.get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        settings.TICKS_GROUP_NAME, {
            "type": 'new_ticks',
            "content": json.dumps(ticks),
        })

我们需要在group_send()包装器中包含对async_to_sync()的调用,因为channel.layers仅提供异步实现,而我们是从同步上下文中调用它。 Django Channels文档中提供了更多详细信息。

注意:

  • 确保“ type”属性与使用者处理程序的名称匹配(即:“ new_ticks”);这是必需的
  • 每个客户都有自己的消费者;因此,当我们在消费者的处理程序中编写self.send()时,这意味着:将数据发送到单个客户端
  • 在这里,我们将数据发送到“组”抽象,然后Channel Layers会将其传递给每个注册的消费者

动机

在某些情况下,轮询仍然是最合适的选择,因为它简单有效。

但是,在某些情况下,您可能会遇到一些限制:

  • 即使没有新数据可用,您仍在查询服务器
  • 您引入了一些延迟(最坏的情况是整个轮询周期)。代价是:更少的延迟=更多的流量。

使用Websocket,您可以改为(仅当有新数据可用时)通过向客户端发送特定消息来通知客户端。

答案 1 :(得分:0)

AJAX调用和REST API是您要寻找的组合。对于实时数据更新,最好的方法是定期轮询REST API。像这样:

    <template>
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card card-default">
                    <div class="card-header">Goals</div>

                    <div class="card-body">
                        This is the goals example
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

现在将Django Rest Framework添加到您的项目中。他们有一个简单的教程here。创建一个API端点,该端点将以JSON形式返回数据,并在AJAX调用中使用该URL。

现在,您可能会感到困惑,因为在从function doPoll(){ $.post('<api_endpoint_here>', function(data) { // Do operation to update the data here setTimeout(doPoll, <how_much_delay>); }); } 视图呈现页面时,您已将数据作为上下文传递到模板中。那将不再起作用。您必须添加一个脚本来更新元素的值,例如

home

其中document.getElementById("element_id").value = "New Value"; 是您赋予元素的ID,element_id是您从AJAX调用的响应中获取的数据。

我希望这会为您提供一个基本的背景。