Server-side processing - 400 Bad request

Server-side processing - 400 Bad request

culterculter Posts: 102Questions: 24Answers: 0

Hi, I'm trying to run this python/flask server-side template (not implementing it to my project):

https://github.com/SergioLlana/datatables-flask-serverside

The client-side table works fine, but the server-side table give me 400 Bad request error. DataTables displays this error:

DataTables warning: table id=serverside_table - Ajax error. For more information about this error, please see http://datatables.net/tn/7

but there is nothing about http error 400 in the documentation. I modified the javascript code as Kevin suggested in some other post from

$(document).ready(function () {
  $('#table_id').DataTable({
    bProcessing: true,
    bServerSide: true,
    sPaginationType: "full_numbers",
    lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
    bjQueryUI: true,
    sAjaxSource: '<API_ENDPOINT>',
    columns: [
      {"data": "Column A"},
      {"data": "Column B"},
      {"data": "Column C"},
      {"data": "Column D"}
    ]
  });
});

to this:

$(document).ready(function () {
  $('#table_id').DataTable({
    processing: true,
    serverSide: true,
    paginationType: "full_numbers",
    lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
    ajax: '/tables/serverside_table,
    columns: [
      {"data": "Column A"},
      {"data": "Column B"},
      {"data": "Column C"},
      {"data": "Column D"}
    ]
  });
});

The 400 error occured with the original code too.
Thank you for any advice.

Answers

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Hi @culter ,

    This SO thread here may help.

    Cheers,

    Colin

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    The place to start is to turn up the debug level on your Flask webserver to see why its returning the 400 Bad Request error. This will give an indication of where to start looking.

    One possibility is the server side script you are using may require HTTP POST instead of GET. If this is the case this example will help:
    https://datatables.net/examples/server_side/post.html

    Kevin

  • culterculter Posts: 102Questions: 24Answers: 0

    Thank you, guys. Good point to turn on debug mode. I'm ashamed. I turned on the debug mode and the 400 Bad request changed to 500 Internal Server Error.

    File "/opt/serverside09_Llana/venv/lib/python3.5/site-packages/werkzeug/datastructures.py", line 1415, in getitem
    raise exceptions.BadRequestKeyError(key)
    werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400 Bad Request: KeyError: 'iSortCol_0'

    the iSortCol_0 is used in the server-side script in this part dedicated to sorting rows:

     def _custom_sort(self, data):
            '''
            Sorts the rows taking in to account the column (or columns) that the
            user has selected.
    
            Args:
                data: Filtered data.
    
            Returns:
                Sorted data by the columns specified by the user.
            '''
            def is_reverse(str_direction):
                ''' Maps the 'desc' and 'asc' words to True or False. '''
                return True if str_direction == 'desc' else False
    
            if (self.request_values['iSortCol_0'] != "") and \
               (self.request_values['iSortingCols'] > 0):
                column_number = int(self.request_values['iSortCol_0'])
                column_name = self.columns[column_number]['column_name']
                sort_direction = self.request_values['sSortDir_0']
                return sorted(data,
                              key=lambda x: x[column_name],
                              reverse=is_reverse(sort_direction))
            else:
                return data
    

    here is the complete server-side script:
    https://github.com/SergioLlana/datatables-flask-serverside/blob/master/app/mod_tables/serverside/serverside_table.py

    I did'nt changed the versions of DataTables this solution is using. Is it good idea to change these links to newest versions?

     <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css"></link>
      <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
      <script type="text/javascript" src="//code.jquery.com/jquery-1.12.4.js"></script>
      <script type="text/javascript" src="//cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js"></script>
    

    Thank you.

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    Those darned KeyErrors :smile:

    Good find. Looks like that script expects the DT 1.9 parameters. According to this section of the docs:
    https://datatables.net/manual/server-side#Legacy

    You will want to use the sAjaxSource option instead of ajax.

    I haven't used that Python script but I suspect using the latest Datatables versions should be ok. Allan can confirm but I don't think there is much difference in the server side requirements between DT 1.10.12 and 1.10.18.

    Kevin

  • culterculter Posts: 102Questions: 24Answers: 0

    Thank you, Kevin. I changed it to sAjaxSource, but I still get the same error. Then I changed the versions to the latest and the result is the same :(

  • culterculter Posts: 102Questions: 24Answers: 0

    I tried to access the path to the data pushed to DataTables - 127.0.0.1/tables/serverside_table and there is this error:

    werkzeug.exceptions.BadRequestKeyError
    werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400 Bad Request: KeyError: 'iSortCol_0'

    The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.
    To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side.

    You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

    dump() shows all variables in the frame
    dump(obj) dumps all that's known about the object

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    With sAjaxSource you should see the parameters sent from the client to /tables/serverside_table as described here:
    https://legacy.datatables.net/usage/server-side

    You can see it by looking at the browser's network tools in the request sent to /tables/serverside_table. And you should be able to print them in your Python function for /tables/serverside_table. Likely the parameter is **kwargs. If **kwargs or whatever the parameter name is with ** you can print it at the top of the function. If you have things like iSortCol_0 in kwargs then follow through the Python scripts to make sure self.request_values gets set with these values.

    If you see them from the client then somewhere in the Python scripts you are losing the parameters.

    Kevin

  • culterculter Posts: 102Questions: 24Answers: 0
  • culterculter Posts: 102Questions: 24Answers: 0

    The author of python/flask server-side script kindly updated his code and now it works with the old .js settings. I tried to update DataTables inicialisation to the newest ones and all of them works except sAjaxSource. When I change it to

    ajax: '/tables/serverside_table',

    the old error 400 Bad Request: KeyError: 'iSortCol_0' comes back. Do you have idea how to modify this row to make it work, or some changes in the script are necessary? Is it ok to leave this 'sAjaxSource' or is it better to make it work with 'ajax'? Thank you.

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    The author of python/flask server-side script kindly updated his code and now it works with the old .js settings

    Not sure what was updated but since the server side script still seems to use the legacy Datatables values I think you will need to use sAjaxSource so it sends the legacy parameters.

    Did you follow the steps above to make sure all the expected server side parameters are being sent and processed by your Flask route function?

    Kevin

  • allanallan Posts: 63,540Questions: 1Answers: 10,476 Site admin

    Try adding the legacy sServerMethod: 'POST' option to your configuration. That will make DataTables send a POST request rather than GET, which as Kevin noted above the script you are using might require.

    Allan

  • culterculter Posts: 102Questions: 24Answers: 0

    Hi Allan. Thank you. I added the sServerMethod: 'POST' to the javascript code, but it ended with 405 (Method not allowed). When I change it to sServerMethod: 'GET', it's working as previous.

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    The parameter it is looking for is in the request: iSortCol_0=0

    From what I can tell your route is here. Specifically:

    @tables.route("/serverside_table", methods=['GET'])
    def serverside_table_content():
        data = table_builder.collect_data_serverside(request)
        return jsonify(data)
    

    Try printing request in this function. I believe this should have all your parameters. Again I'm not familiar with Flask nor this library. Will try to help as much as possible but you might be better off posting your question on SO. It seems the current problem is how to use the SergioLlana Datatalbes Flask library. At this point Datatables is working as it should.

    Kevin

This discussion has been closed.