Search code examples
jquerypython-3.xdjangoajaxcontextmenu

How to allow a POST request to proceed, while running multiple GET requests in the background?


I have a table that loads empty (no rows) and then I run an Ajax call -inside a django for-loop- to populate the table with data from a python function that each time returns one row/item.

I also have a jquery function that when I right-click on a row, it produces a context menu for that specific row, populated with options dynamically through an AJAX script. The problem is that the context menu isnt populated promptly.

How it works is:

  • I right-click on a row and "grab" certain parameters of the object in said row.
  • These parameters then are passed to the getTablePerms() function, which runs an AJAX POST request to a Python function that -based on those parameters- returns the permissions of the item in the row and modifies the html of the context menu div, essentially showing the context menu options.
  • While the rows are added to the table, right-clicking on a row should produce the context menu immediately, but it does not. It appears as if its waiting to get response from most (if not all) of the GET requests.

To counter this I applied two setTimeouts to make the AJAX GET request wait a few seconds, but while it works on small amounts of rows (70 rows), at larger amounts (500+ rows) it does not show the context menu until it has finished receiving the STATUS-200 responses. The POST request does get sent, but it is its response that gets delayed.

Which is why I think that the sequential GET requests might block the POST request from receiving the response because it gets queued after the GET request responses.

My views.py

def myprotocol_item(request,pk):

    data = dict()
    protocoltype = request.GET.get('protocoltype')
    mytype = request.GET.get('type')
    queryset = Myprotocol.objects.filter(Q(pk=pk) & Q(del_f=0))
    context = { 'myprot':queryset[0]}  
    template = 'protocol/Myprotocol/list_table_body.html'
    data['html_form'] = render_to_string(template,context,request=request,)
    data['pk'] = pk
    return JsonResponse(data)

My ajax call:

<script>
{% if data %}
  {% for dataitem in data %}
  setTimeout(function(){
    $.ajax({
        headers: {'X-CSRFToken':getCookie('csrftoken')},
        url: "{% url 'protocol:myprotocol-item' dataitem.protocol_ptr.id %}",
        type: 'get',
        dataType: 'json',
        success: function(data) {
          var tablerows = $('#myTable1 #tbody tr').length;
         
          if (tablerows === 0){
            $("#myTable1 #tbody").append(data.html_form);  
          }
          else if (tablerows > 0){
           
            $("#myTable1 #tbody tr").last().after(data.html_form);

          }
          // let the plugin know that we made a update
          // the resort flag set to anything BUT false (no quotes) will trigger an automatic
          // table resort using the current sort          
          var resort = true;
          $("table").trigger("update", [resort]); 
          
   
        }, // end of success
        error : function(xhr,errmsg,err) {
          console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
        } // end of error   

    }); //end of ajax
  }
  ,3000);//end of SetTimeout
  {% endfor %}
{% endif %}
</script>

My rightclick.js

  $('body').contextmenu(function() {
    return false;
  });
  //==============contextMenu====================== //
  var $contextMenu = $("#contextMenu");
  var $id_page_content = $("#id_page_content");
  var $myTable1 = $("#myTable1");
  $('body').on("contextmenu", "#myTable1 tbody tr,#myTable2 tr",function(e) {

    var myid = $(this).attr('id').split('_')[1].replace('.', '');
    var mytype = $(this).attr('id').split('_')[2];
    var f = $(this).attr('id').split('_')[3];
    var mycontainerid = $(this).attr('id').split('_')[4];

    var obj_table = $(this).attr('data-obj-table').split('_')[1];
    var routeid = $(this).attr('data-obj-table').split('_')[2];
    

    console.log('myid '+ myid);
    console.log('folder ' + f);
    console.log('mytype ' + mytype);
    console.log('obj table ' + obj_table);
    console.log('obj route ' + routeid);
    console.log('mycontainerid ' + mycontainerid);

    getTablePerms(myid,mytype,obj_table,f,routeid,mycontainerid);
    
    if ($(window).scrollTop() < $(document).height() && e.pageY > $myTable1.height()-80 && e.pageY >= document.querySelector('[id^="myTable"]').offsetTop+200 && e.pageY >= $(window).height()-300){
    
       $contextMenu.css({
         display: "block",
         left: e.pageX,
         top: e.pageY-248,
    
       });
     }
     else {
      $contextMenu.css({
        display: "block",
        left: e.pageX,
        top: e.pageY,

      });
    }
    
  });
  $('#contextMenu').click( function(event){
    event.stopPropagation();
    $('#contextMenu').hide();
  });
  $('body').click( function(){
    
    $('#contextMenu').hide();
  });
  //==============End contextMenu====================== //

function getTablePerms(myid,mytype,obj_table,f,routeid,mycontainerid){
        $.ajax({
              type:"POST",
              dataType: "json",
              url: "/common/get/object/perms/",
              data:{'csrftoken':getCookie('csrftoken'),'obj':myid,'mytype':mytype,'obj_table':obj_table,'f':f,'routeid':routeid,'mycontainerid':mycontainerid},
              success: function(result)
              {
//========== JQUERY CODE TO MODIFY CONTEXT MENU ============//
              }// end of success
        });// end of ajax
}// end of function

Any thoughts on how to make the POST request reply be given priority in receiving it?


Solution

  • The answer came after a bit of search in the form of AjaxQueue.

    The problem was basically that while the data are called in one Ajax request, it doesnt allow any other calls made, including the Ajax POST that builds the context menu depending on each item permissions.

    What I did instead is to make the Ajax request to bring the data of each table row into an AjaxQueue, by adding the ajaxQueue.js to the list template's header and modifying the ajax request as follows:

    {% if data %}
      {% for dataitem in data %}
        var jqXHR = $.ajaxQueue({
            headers: {'X-CSRFToken':getCookie('csrftoken')},
            url: "{% url 'protocol:myprotocol-item' dataitem.protocol_ptr.id %}",
            type: 'get',
            dataType: 'json',
            success: function(data) {
              var tablerows = $('#myTable1 #tbody tr').length;
             
              if (tablerows === 0){
                $("#myTable1 #tbody").append(data.html_form);  
              }
              else if (tablerows > 0){
               
                $("#myTable1 #tbody tr").last().after(data.html_form);
    
              }
            
              var resort = true;
              $("table").trigger("update", [resort]); 
              return false;          
       
            }, // end of success
            error : function(xhr,errmsg,err) {
              console.log(xhr.status + ": " + xhr.responseText);
            } // end of error   
    
        }); //end of ajax
      }
    
      {% endfor %}
    {% endif %}
    

    The ajaxQueue calls each item separately making the process fast enough without binding the server resources and allows for irrelevant requests to be made, including the one for the context menu.

    A big thank you to @gnarf and their ajaxQueue script!!!