我正在尝试开发一种拍卖类型系统,其中客户下订单,然后不同的商店可以为该订单提供价格。
这个系统的一个有趣的部分是,当最初创建订单时,可用的商店将有60秒来提供他们各自的报价。当第一家商店提出要约时,"拍卖"现在只有接下来的20秒才能让其他商店自己报价。如果他们确实提出了另一个报价,那么在这个较小的分配时间内,这20秒就会刷新。只要有足够的时间,优惠就可以继续收到,不能超过给定的最初60秒。
class Order(models.Model):
customer = models.ForeignKey(Customer)
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now_add=True)
total = models.FloatField(default=0)
status = models.IntegerField(default=0)
delivery_address = models.ForeignKey(DeliveryAddress)
store = models.ForeignKey(Store, null=True, blank=True, related_name='orders', on_delete=models.CASCADE)
credit_card = models.ForeignKey(CreditCard, null=True, blank=True, related_name='orders')
class OrderOffer(models.Model):
store = models.ForeignKey(Store, related_name="offers", on_delete=models.CASCADE)
order = models.ForeignKey(Order, related_name="offers", on_delete=models.CASCADE)
create_time = models.DateTimeField(auto_now_add=True)
除了这些要求之外,我还希望在新的优惠实时到达时更新客户端。为此,我使用django-channels
实现WebSockets。
我有以下consumers.py
文件:
from channels.generic.websockets import WebsocketConsumer
from threading import Timer
from api.models import Order, OrderOffer
from django.db.models.signals import post_save
from django.dispatch import receiver
class OrderConsumer(WebsocketConsumer):
def connect(self, message, **kwargs):
"""
Initialize objects here.
"""
order_id = int(kwargs['order_id'])
self.order = Order.objects.get(id=order_id)
self.timer = Timer(60, self.sendDone)
self.timer.start()
self.message.reply_channel.send({"accept": True})
def sendDone(self):
self.send(text="Done")
# How do I bind self to onOffer?
@receiver(post_save, sender=OrderOffer)
def onOffer(self, sender, **kwargs):
self.send(text="Offer received!")
if (len(self.offers) == 0):
self.offerTimer = Timer(20, self.sendDone)
self.offers = [kwargs['instance'],]
else:
self.offerTimer = Timer(20, self.sendDone)
self.offers.append(kwargs['instance'])
def receive(self, text=None, bytes=None, **kwargs):
# Echo
self.send(text=text, bytes=bytes)
def disconnect(self, message, **kwargs):
"""
Perform necessary disconnect operations.
"""
pass
我已经成功地在我的客户端和服务器之间建立了一个WebSocket通信通道。我测试了发送消息,一切似乎都没问题。现在我想检测新OrderOffer
的创建,并向客户端发送通知。为此,我需要访问self
变量,使用self.send
,这是不可能的,因为信号装饰器不会发送此参数。我通过声明onOffer with self尝试强制它,但是我收到以下错误:
TypeError: onOffer() missing 1 required positional argument: 'self'
如果我能以某种方式访问关键字参数,那就是信号集,我可能会做类似的事情:
context = self
。
我会感谢任何帮助,甚至是我原来问题的替代解决方案。
答案 0 :(得分:3)
如果有人偶然发现,这就是我在signals.py
中解决它的方式。我有一个Job
,每次更改时都需要将status
发送给客户端。这是我的signals.py
:
import channels.layers
from asgiref.sync import async_to_sync
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Job
def send_message(event):
'''
Call back function to send message to the browser
'''
message = event['text']
channel_layer = channels.layers.get_channel_layer()
# Send message to WebSocket
async_to_sync(channel_layer.send)(text_data=json.dumps(
message
))
@receiver(post_save, sender=Job, dispatch_uid='update_job_status_listeners')
def update_job_status_listeners(sender, instance, **kwargs):
'''
Sends job status to the browser when a Job is modified
'''
user = instance.owner
group_name = 'job-user-{}'.format(user.username)
message = {
'job_id': instance.id,
'title': instance.title,
'status': instance.status,
'modified': instance.modified.isoformat(),
}
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'send_message',
'text': message
}
)
顺便说一句,我有一个消费者class JobUserConsumer(AsyncWebsocketConsumer)
,我在其中定义了这些组:
async def connect(self):
user = self.scope["user"]
self.group_name = 'job-user-{}'.format(user.username)
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
我使用的项目在这里:https://github.com/ornl-ndav/django-remote-submission/tree/master/django_remote_submission
答案 1 :(得分:1)
如果您想从“外部”与消费者交谈 - 在这种情况下,从模型保存方法 - 您需要使用渠道层与之交谈:http://channels.readthedocs.io/en/latest/topics/channel_layers.html
基本上,你需要:
OrderOffer
新的自定义type
时向群组发送消息,例如{"type": "order.new_offer", "order_offer_id": 45}
def order_new_offer(self, event):
self.send
来讨论套接字(如果您需要额外的信息发送到未放入事件消息的客户端,则查询数据库)。您可以在MultiChat示例项目中看到此变体:https://github.com/andrewgodwin/channels-examples/tree/master/multichat
答案 2 :(得分:1)
对于那些仍然有Web套接字问题的人,这可能会有所帮助:
from api.models import Order, OrderOffer
from asgiref.sync import async_to_sync
import channels.layers
from channels.generic.websocket import JsonWebsocketConsumer
from django.db.models import signals
from django.dispatch import receiver
class OrderOfferConsumer(JsonWebsocketConsumer):
def connect(self):
async_to_sync(self.channel_layer.group_add)(
'order_offer_group',
self.channel_name
)
self.accept()
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
'order_offer_group',
self.channel_name
)
self.close()
def receive_json(self, content, **kwargs):
print(f"Received event: {content}")
def events_alarm(self, event):
self.send_json(event['data'])
@staticmethod
@receiver(signals.post_save, sender=OrderOffer)
def order_offer_observer(sender, instance, **kwargs):
layer = channels.layers.get_channel_layer()
async_to_sync(layer.group_send)('order_offer_group', {
'type': 'events.alarm',
'data': {
'text': 'Offer received',
'id': instance.pk
}
})
在urls.py中,您需要注册一个新的webscoket路由:
websocket_urlpatterns = [url(r'^order_offer$', OrderOfferConsumer)]