编辑:现在转移到 django-channels,效果很好,但比下面的解决方案更复杂。
以前的:
好的,下面是我现在如何解决它的伪代码。基本上我用https://pusher.com/docs/javascript_quick_start服务器端将实例化的对象传递给compute_module
。一个缺点是推送消息是短暂的,所以我必须做一些额外的工作LogPusher
将它们存储在数据库中,改天再做……
另外,在我的实际实现中,我通过$.post()
ajax 调用$(document).ready()
因为小任务完成得如此之快,用户永远不会看到推送消息,因为连接尚未建立(回到历史消息问题)。
我上面没有提到的另一种替代路线是https://channels.readthedocs.io/en/latest/
[编辑]另一个解决方案是服务器发送的事件其中有Django 实现,没测试过。但它看起来很适合单向更新,例如从服务器到客户端(相对于 websockets 双向)。你需要一个像这样的消息系统Redis 发布订阅获取服务器 sse 路由的更新。
通过推送器从 django 服务器进行前端更新:
# views.py
from tasks import run_task
def view_task():
run_task.delay('event')
return render(request, 'template.html', 'pusher_event':'event')
# tasks.py
import pusher
from django.conf import settings
from compute_module import compute_fct
class LogPusher(object):
def __init__(self, event):
self.pusher_client = pusher.Pusher(app_id=settings.PUSHER_APP_ID,
key=settings.PUSHER_KEY,
secret=settings.PUSHER_SECRET,
cluster=settings.PUSHER_CLUSTER, ssl=True)
self.event = event
def send(self, data):
self.pusher_client.trigger(settings.PUSHER_CHANNEL, self.event, json.dumps(data))
@shared_task
def run_task(pusher_event):
log_pusher = LogPusher(pusher_event)
result = compute_fct(log_pusher)
# how to catch status update messages from compute_module while compute_fct is running??
if result == 'error':
log_pusher.send('status':'error')
else:
log_pusher.send('status':'success')
# compute_module.py
import pandas as pd
def compute_fct(log_pusher):
# send message: status = loading file
log_pusher.send('status':'loading file')
df = pd.read_csv('test.csv')
# send message: status = computing
log_pusher.send('status':'computing')
val = df['col'].mean()
if val is None:
return {'status':'error'}
else:
return {'status':'success','val':val}
# context_processors.py
# see https://stackoverflow.com/questions/433162/can-i-access-constants-in-settings-py-from-templates-in-django
from django.conf import settings
def pusher(request):
return {'PUSHER_KEY': settings.PUSHER_KEY, 'PUSHER_CLUSTER': settings.PUSHER_CLUSTER , 'PUSHER_CHANNEL': settings.PUSHER_CHANNEL }
# template.html
<script>
var pusher = new Pusher("{{PUSHER_KEY}}", {
cluster: "{{PUSHER_CLUSTER}}",
encrypted: true
});
var channel = pusher.subscribe("{{PUSHER_CHANNEL}}");
channel.bind("{{pusher_event}}", function(data) {
// process data
});
</script>