顧客が注文すると、さまざまな店舗がその注文の価格を提示できるオークションタイプのシステムを開発しようとしています。
このシステムの興味深い部分は、注文が最初に作成されたときに、利用可能なストアがそれぞれのオファーを行うのに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)
_
これらの要件に加えて、新しいオファーがリアルタイムで到着したときにクライアントを更新したいと思います。このために、私はWebSocketの_Django-channels
_実装を使用しています。
私は次の_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をselfで宣言して強制しようとしましたが、次のエラーが発生します。
TypeError: onOffer() missing 1 required positional argument: 'self'
セットを通知するキーワード引数に何らかの方法でアクセスできれば、_context = self
_のようなことができるかもしれません。
私の元の問題に対する助け、あるいは代替の解決策さえもいただければ幸いです。
誰かがそれに遭遇した場合、これは私が_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
「外部」から(この場合はモデルの保存メソッドから)コンシューマーと通信する場合は、チャネルレイヤーを使用して通信する必要があります。 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
それでも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)]