目前,我有一个元素,当单击该元素时,会设置一个全局冷却计时器,该计时器会影响使用 Django websockets 的所有客户端。我的问题是,虽然最初 websocket 值通过 componentDidMount
在我的 React 客户端中转换为 state
,但当其值实时更改时,websocket 不会再次运行。
这是它的详细工作原理。
计时器通过 django 模型更新,我通过 websocket 将其广播到我的 React 前端:
consumer.py
class TestConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("connected", event)
await self.send({
"type":"websocket.accept",
})
#correct way to grab the value btw, just work on outputting it so its streaming
@database_sync_to_async
def get_timer_val():
val = Timer.objects.order_by('-pk')[0]
return val.time
await self.send({
"type": "websocket.send",
"text": json.dumps({
'timer':await get_timer_val(),
})
})
async def websocket_receive(self, event):
print("received", event)
async def websocket_disconnect(self, event):
print("disconnected", event)
这最初是有效的,因为我的 React 客户端启动并将值转换为 state
,其中:
组件.jsx
//handles connecting to the webclient
componentDidMount() {
client.onopen = () => {
console.log("WebSocket Client Connected");
};
client.onmessage = (message) => {
const myObj = JSON.parse(message.data);
console.log(myObj.timer);
this.setState({ timestamp: myObj.timer });
};
}
//handles submitting the new timer upon clicking on element
handleTimer = () => {
// handles making PUT request with updated cooldown timer upon submission,
const timestamp = moment().add(30, "minutes");
const curr_time = { time: timestamp };
axios
.put(URL, curr_time, {
auth: {
username: USR,
password: PWD,
},
})
.then((res) => {
console.log(res);
});
};
//button that prompts the PUT request
<button
type="submit"
onClick={(e) => {
this.handleTimer();
//unrelated submit function
this.handleSubmit(e);
}}
>
Button
</button>
但是,当用户单击操纵的元素并且数据库模型发生更改时,网络套接字值不会发生变化,直到我刷新页面。我认为问题是我只在连接期间发送 websocket 数据,但我不知道如何保持该“连接”打开,以便任何更改都会自动发送到客户端服务器。我浏览了大量的链接来寻找实现实时的最佳方法,但其中大多数要么是关于 socket.io 的,要么是实现聊天应用程序的。我想做的就是将 django models 值实时传输到前端。
最佳答案
当您想要将其他代码触发的更新发送到 websocket 连接时,django-channels
的 channels
部分就会发挥作用。它的工作原理如下:
- 连接时,您将 websocket 添加到某个命名组
- 当 Timer 的值发生变化时,您可以通过触发更改的代码将具有特定
类型
的事件(通过 channel 层)发送到该组。 - Django-channels 然后调用以组中每个 websocket 的事件类型命名的 Consumer 方法
- 最后,在此方法中,您的代码将消息发送到客户端
您需要使用 Redis 配置 channel 层。 https://channels.readthedocs.io/en/stable/topics/channel_layers.html
现在,一步一步来。我将省略不相关的部分。
1
async def websocket_connect(self, event):
await self.send({
"type":"websocket.accept"
})
await self.channel_layer.group_add('timer_observers', self.channel_name)
2 这里我在模型内部发送事件,但是您可以在 View 中或通过 django 信号执行此操作,无论您想要什么。另外,我没有检查该值是否实际更改,并且我假设数据库中只有一个 Timer 实例。
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
class Timer(models.Model):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
async_to_sync(get_channel_layer().send)(
'timer_observers', {"type": "timer.changed"}
)
3+4 我已经提取了时间发送代码以供重复使用
class TestConsumer(AsyncConsumer):
async def websocket_connect(self, event):
print("connected", event)
await self.send({
"type": "websocket.accept",
})
await self.channel_layer.group_add('timer_observers', self.channel_name)
await self.send_current_timer()
async def timer_changed(self, event):
await self.send_current_timer()
async def send_current_timer(self):
@database_sync_to_async
def get_timer_val():
val = Timer.objects.order_by('-pk')[0]
return val.time
await self.send({
"type": "websocket.send",
"text": json.dumps({
'timer': await get_timer_val(),
})
})
这里的想法是,您以与来自客户端的外部事件相同的方式处理应用程序生成的内部事件,即websocket.connect -> async def websocket_connect
。因此,channels
层会向您“发送”一条“websocket 消息”,然后您进行响应(但发送给实际的客户端)。
我希望这有助于理解这些概念。也许你正在做的事情有点矫枉过正,但我认为这只是一个学习练习=) 我不能 100% 确定这会起作用,所以请随时提出其他问题。
关于reactjs - 通过 Django websockets 将更新数据传输到客户端的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67992179/