def _datatable(self, r, **attr):
        """
            Get a data table

            @param r: the S3Request
            @param attr: parameters for the method handler
        """

        # Check permission to read in this table
        authorised = self._permitted()
        if not authorised:
            r.unauthorised()

        resource = self.resource
        get_config = resource.get_config

        # Get table-specific parameters
        linkto = get_config("linkto", None)

        # List ID
        list_id = attr.get("list_id", "datatable")

        # List fields
        list_fields = resource.list_fields()

        # Default orderby
        orderby = get_config("orderby", None)

        response = current.response
        s3 = response.s3
        representation = r.representation

        # Pagination
        get_vars = self.request.get_vars
        if representation == "aadata":
            start, limit = self._limits(get_vars)
        else:
            # Initial page request always uses defaults (otherwise
            # filtering and pagination would have to be relative to
            # the initial limits, but there is no use-case for that)
            start = None
            limit = None if s3.no_sspag else 0

        # Initialize output
        output = {}

        # Linkto
        if not linkto:
            linkto = self._linkto(r)

        left = []
        distinct = False
        dtargs = attr.get("dtargs", {})

        if r.interactive:

            # How many records per page?
            if s3.dataTable_pageLength:
                display_length = s3.dataTable_pageLength
            else:
                display_length = 25

            # Server-side pagination?
            if not s3.no_sspag:
                dt_pagination = "true"
                if not limit:
                    limit = 2 * display_length
                current.session.s3.filter = get_vars
                if orderby is None:
                    dt_sorting = {"iSortingCols": "1",
                                  "sSortDir_0": "asc"
                                  }

                    if len(list_fields) > 1:
                        dt_sorting["bSortable_0"] = "false"
                        dt_sorting["iSortCol_0"] = "1"
                    else:
                        dt_sorting["bSortable_0"] = "true"
                        dt_sorting["iSortCol_0"] = "0"

                    q, orderby, left = resource.datatable_filter(list_fields,
                                                                 dt_sorting)
            else:
                dt_pagination = "false"

            # Get the data table
            dt, totalrows, ids = resource.datatable(fields=list_fields,
                                                    start=start,
                                                    limit=limit,
                                                    left=left,
                                                    orderby=orderby,
                                                    distinct=distinct)
            displayrows = totalrows

            if not dt.data:
                # Empty table - or just no match?
                #if dt.empty:
                #    datatable = DIV(self.crud_string(resource.tablename,
                #                                     "msg_list_empty"),
                #                    _class="empty")
                #else:
                #    #datatable = DIV(self.crud_string(resource.tablename,
                #                                      "msg_no_match"),
                #                     _class="empty")

                # Must include export formats to allow subsequent unhiding
                # when Ajax (un-)filtering produces exportable table contents:
                #s3.no_formats = True

                if r.component and output in "showadd_btn":
                    # Hide the list and show the form by default
                    del output["showadd_btn"]
                    datatable = ""

            # Always show table, otherwise it can't be Ajax-filtered
            # @todo: need a better algorithm to determine total_rows
            #        (which excludes URL filters), so that datatables
            #        shows the right empty-message (ZeroRecords instead
            #        of EmptyTable)
            dtargs["dt_pagination"] = dt_pagination
            dtargs["dt_pageLength"] = display_length
            dtargs["dt_base_url"] = r.url(method="", vars={})
            dtargs["dt_permalink"] = r.url()
            datatable = dt.html(totalrows,
                                displayrows,
                                id=list_id,
                                **dtargs)

            # View + data
            response.view = self._view(r, "list_filter.html")
            output["items"] = datatable

        elif representation == "aadata":

            # Apply datatable filters
            searchq, orderby, left = resource.datatable_filter(list_fields,
                                                               get_vars)
            if searchq is not None:
                totalrows = resource.count()
                resource.add_filter(searchq)
            else:
                totalrows = None

            # Orderby fallbacks
            if orderby is None:
                orderby = get_config("orderby", None)

            # Get a data table
            if totalrows != 0:
                dt, displayrows, ids = resource.datatable(fields=list_fields,
                                                          start=start,
                                                          limit=limit,
                                                          left=left,
                                                          orderby=orderby,
                                                          distinct=distinct,
                                                          getids=False)
            else:
                dt, displayrows = None, 0
            if totalrows is None:
                totalrows = displayrows

            # Echo
            draw = int(get_vars.get("draw", 0))

            # Representation
            if dt is not None:
                output = dt.json(totalrows,
                                 displayrows,
                                 list_id,
                                 draw,
                                 **dtargs)
            else:
                output = '{"recordsTotal":%s,' \
                         '"recordsFiltered":0,' \
                         '"dataTable_id":"%s",' \
                         '"draw":%s,' \
                         '"data":[]}' % (totalrows, list_id, draw)

        else:
            r.error(415, current.ERROR.BAD_FORMAT)

        return output

    # -------------------------------------------------------------------------
dataset/ETHPy150Open sahana/eden/modules/s3/s3crud.py/S3CRUD._datatable/swapped operands of `in`