在自定义命令中与 Django 通道进行数据绑定

Posted

技术标签:

【中文标题】在自定义命令中与 Django 通道进行数据绑定【英文标题】:Databinding with Django channels in custom command 【发布时间】:2016-12-06 12:53:21 【问题描述】:

我开始使用 Django 频道。我需要我的模型设备的绑定数据。我做。当我在管理界面中更改设备对象时,我收到有关在客户端(Web 浏览器)上更改的消息,但我也通过自定义 django 管理命令运行简单的 WebsocketServer。当在自定义命令过程中更改了设备对象时,我没有在客户端收到任何消息。我该如何解决?

devices/models.py

OFFLINE = 0
ONLINE = 1
REGISTRATION = 2
REMOVED = 3
ERROR = 4


STATUS_CHOICES = (
    (OFFLINE, _("Offline")),
    (ONLINE, _("Online")),
    (REGISTRATION, _("Registration")),
    (REMOVED, _("Removed")),
    (ERROR, _("Error")),
)


class Device(models.Model):
    name = models.CharField(max_length=128,
                            null=True, blank=True, default=None, verbose_name=_("Name"))
    created = models.DateTimeField(verbose_name=_("Creation date time"), editable=False)
    modified = models.DateTimeField(verbose_name=_("Last change date time"), blank=True)

    removed = models.BooleanField(verbose_name=_("Device has been removed"), default=False)
    remove_date = models.DateTimeField(verbose_name=_("Date of Device removing"),
                                       null=True, blank=True,
                                       default=None)

    joined = models.BooleanField(verbose_name=_("Registration status"), default=False)
    date_joined = models.DateTimeField(verbose_name=_("Registration date time"), blank=True, null=True, default=None)
    owner = models.ForeignKey("accounts.User",
                              null=True, blank=True, default=None,
                              verbose_name=_("Who registered"), on_delete=models.SET(None))

    status = models.SmallIntegerField(choices=STATUS_CHOICES, blank=True, default=REGISTRATION, verbose_name=_("Status"))
    frozen = models.BooleanField(default=False, verbose_name=_("Device is frozen"))

devices/databinding.py

from channels.binding.websockets import WebsocketBinding

from .models import Device


class DeviceBinding(WebsocketBinding):

    model = Device
    stream = "device"
    fields = ['status', 'joined', 'frozen']

    @classmethod
    def group_names(cls, *args, **kwargs):
        return ["binding.devices"]

    def has_permission(self, user, action, pk):
        return True  # FIXME: check permission for user

project/routing.py

from channels import route_class, route
from devices.consumers import Demultiplexer
from devices.databinding import DeviceBinding

channel_routing = [
    route_class(Demultiplexer, path='^/stream/?$'),
    route("binding.device", DeviceBinding.consumer),
]

devices/consumers.py

from channels.generic.websockets import WebsocketDemultiplexer


class Demultiplexer(WebsocketDemultiplexer):

    mapping = 
        "device": "binding.device",
    

    def connection_groups(self):
        return ["binding.devices"]

我运行自定义高速公路 WebSockerServer 的简单自定义命令。

项目/管理/命令/runcustomcommand.py

from django.core.management import BaseCommand
from project.websocketserver.autobahn_server import WebSocketServer

class Command(BaseCommand):
    help = "Run WebSocket Server"

    # A command must define handle()
    def handle(self, *args, **options):
        wss = WebSocketServer()
        wss.start()

项目/websocketserver/autobahn_server.py

类WebSocketServer(对象):

def __init__(self):
    import sys

    from twisted.python import log

    log.startLogging(sys.stdout)

    self.factory = WebSocketServerFactory(
            "ws://host:port".format(
                    host=settings.WSS_HOST, port=settings.WSS_PORT),
            # debug=False
    )
    self.factory.protocol = MyServerProtocol
    self.factory.setProtocolOptions(
            autoPingInterval=settings.WSS_AUTO_PING_INTERVAL,
            autoPingTimeout=settings.WSS_AUTO_PING_TIMEOUT
    )

def start(self):
    from twisted.internet import reactor

    try:
        logger.info("\tSTART SERVER")
        reactor.listenTCP(settings.WSS_PORT, self.factory)
        reactor.run()
    except KeyboardInterrupt:
        pass
    finally:
        self.stop()

def stop(self):
    from twisted.internet import reactor
    reactor.stop()
    logger.info("\tSTOP SERVER")

客户端连接(js代码):

var ws_path = "ws://127.0.0.1:8001/stream/";
console.log("Connecting to " + ws_path);
var webSocket = new WebSocket(ws_path);

webSocket.onopen = function() 
alert("Connected!.");
;

webSocket.onmessage = function(message) 
var data = JSON.parse(message.data);
alert(data);
;

webSocket.onerror = function(error) 
alert("Error: " + error.message);
;

我跑:

redis-server
daphne project.asgi:channel_layer --port 8001
python manage.py runworker -v2
python manage.py runserver 0.0.0.0:8000  -v2

python manage.py runcustomcommand

更新

我无意中发现,如果在我的管理命令的开头添加此代码,随后对对象的更改就会到达客户端:

from channels import Channel
Channel("binding.devices").send()

这就像某种初始化。但这很奇怪。

【问题讨论】:

【参考方案1】:

我也有同样的情况,但我不能肯定地说你的 DeviceBinding.group_namesDemultiplexer.connection_groups 方法没有返回列表。确保它们有尾随逗号["binding.device", ]["binding.devices", ]。除此之外,我能看到的唯一区别是我使用了不同的组名,因为我有多个要绑定的模型,并且为简单起见使用了一个组。

希望对您有所帮助, 斯图

【讨论】:

以上是关于在自定义命令中与 Django 通道进行数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin Forms 命令在自定义控件中始终为空

在自定义模板标签中解析 Django 自定义模板标签

在自定义绑定中Knockout ObservableArray

如何在自定义 django 表单中隐藏 django 标签?

Django:在自定义 URL 后面提供媒体服务

如何在自定义页面 Django 中使用管理表单