Wednesday, December 28, 2016

How to pass data between Django module/app functions without using database in asynchronous web service

I've got a web service under development that uses Django and Django Channels to send data across websockets to a remote application. The arrangement is asynchronous and I pass information between the 2 by sending JSON formatted commands across websockets and then receive replies back on the same websocket.

The problem I'm having is figuring out how to get the replies back to a Javascript call from a Django template that invokes a Python function to initiate the JSON websocket question. Since the command question & data reply happen in different Django areas and the originating Javascript/Python functions call does not have a blocking statement, the Q&A are basically disconnected and I can't figure out how to get the results back to the browser.

Right now, my idea is to use Django global variables or store the results in the Django models. I can get either to work, but I beleive the Django global variables would not scale beyond multiple workers from runserver or if the system was eventually spread across multiple servers.

But since the reply data is for different purposes (for example, list of users waiting in a remote lobby, current debugging levels in remote system, etc), the database option seems unworkable because the reply data is varying structure. That, plus the replies are temporal and don't need to be permanently stored in the database.

Here's some code showing the flow. I'm open to different implementation recommendations or a direct answer to the question of how to share information between the 2 Django functions.

In the template, for testing, I just have a button defined like this:

    <button id="request_lobby">Request Lobby</button>


With a Javascript function. This function is incomplete as I've yet to do anything about the response (because I can't figure out how to connect it):

            $("#request_lobby").click(function(){
                $
.ajax({
                    type
: "POST",
                    url
: "{% url 'test_panel_function' %}",
                    data
: { csrfmiddlewaretoken: '{{ csrf_token }}', button:"request_lobby" },
                    success
: function(response){
                   
}
               
});
           
});


This is the Django/Python function in views.py . The return channel for the remote application is pre-stored in the database as srv.server_channel when the websocket is initially connected (not shown):

    @login_required
   
def test_panel_function(request):

        button
= request.POST.get('button', '')

       
if button == "request_lobby" :
            srv
= Server.objects.get(server_key="1234567890")
            json_res
= []
            json_res
.append({"COMMAND": "REQUESTLOBBY"})
            message
= ({
               
"text": json.dumps(json_res)
           
})
           
Channel(srv.server_channel).send(message)
           
return HttpResponse(button)



Later, the remote application sends the reply back on the websocket and it's received by a Django Channels demultiplexer in routing.py :

   
class RemoteDemultiplexer(WebsocketDemultiplexer):
        mapping
= {
           
"gLOBBY"   : "gLOBBY.receive",
       
}
        http_user
= True
        slight_ordering
= True
         
       
    channel_routing
= [
        route_class
(RemoteDemultiplexer, path=r"^/server/(?P<server_key>[a-zA-Z0-9]+)$"),
        route
("gLOBBY.receive"   , command_LOBBY),
   
]



And the consumer.py :

   
@channel_session    
   
def command_LOBBY(message):
        skey
= message.channel_session["server_key"]
       
for x in range(int(message.content['LOBBY'])):
            logger
.info("USERNAME:  " + message.content[str(x)]["USERNAME"])
            logger
.info("LOBBY_ID:  " + message.content[str(x)]["LOBBY_ID"])
            logger
.info("OWNER_ID:  " + message.content[str(x)]["IPADDRESS"])
            logger
.info("DATETIME:  " + message.content[str(x)]["DATETIME"])


So I need to figure out how to get the reply data in command_LOBBY to the Javascript/Python function call in test_panel_function

Current ideas, both of which seem bad and why I think I need to ask this question for SO:

1) Use Django global variables:

Define in globals.py:

    global_async_result
= {}



And include in all relevant Django modules:

    from test.globals import global_async_result


In order to make this work, when I originate the initial command in test_panel_function to send to the remote application (the REQUESTLOBBY), I'll include a randomized key in the JSON message which would be round-tripped back to command_LOBBY and then global_async_result dictionary would be indexed with the randomized key.

In test_panel_function , I would wait in a loop checking a flag for the results to be ready in global_async_result and then retrieve them from the randomized key and delete the entry in global_async_result.

Then the reply can be given back to the Javascript in the Django template.

That all makes sense to me, but uses global variables (bad), and seems that it wouldn't scale as the web service is spread across servers.

2) Store replies in Django mySQL model.py table

I could create a table in models.py to hold the replies temporarily. Since Django doesn't allow for dynamic or temporary table creations on the fly, this would have to be a pre-defined table.

Also, because the websocket replies would be different formats for different questions, I could not know in advance all the fields ever needed and even if so, most fields would not be used for differing replies.

My workable idea here is to create the reply tables using a field for the randomized key (which is still routed back round-trip through the websocket) and another large field to just store the JSON reply entirely.

Then in test_panel_function which is blocking in a loop waiting for the results, pull the JSON from the table, delete the row, and decode. Then the reply can be given back to the Javascript in the Django template.

3) Use Django signals

Django has a signals capability, but the response function doesn't seem to be able to be embedded (like inside test_panel_function) and there seems to be no wait() function available for an arbitrary function to just wait for the signal. If this were available, it would be very helpful

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/728d41d3-2fbf-405f-bf5a-b861deaf42be%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment