def _datatable(self, r, widget, **attr):
        """
            Generate a data table.

            @param r: the S3Request instance
            @param widget: the widget definition as dict
            @param attr: controller attributes for the request

            @todo: fix export formats
        """

        # Parse context
        context = widget.get("context")
        tablename = widget.get("tablename")
        resource, context = self._resolve_context(r, tablename, context)

        # List fields
        list_fields = widget.get("list_fields")
        if not list_fields:
            # @ToDo: Set the parent so that the fkey gets removed from the list_fields
            #resource.parent = s3db.resource("")
            list_fields = resource.list_fields()

        # Widget filter option
        widget_filter = widget.get("filter")
        if widget_filter:
            resource.add_filter(widget_filter)

        # Use the widget-index to create a unique ID
        list_id = "profile-list-%s-%s" % (tablename, widget["index"])

        # Default ORDERBY
        # - first field actually in this table
        def default_orderby():
            for f in list_fields:
                selector = f[1] if isinstance(f, tuple) else f
                if selector == "id":
                    continue
                rfield = resource.resolve_selector(selector)
                if rfield.field:
                    return rfield.field
            return None

        # Pagination
        representation = r.representation
        get_vars = self.request.get_vars
        if representation == "aadata":
            start = get_vars.get("displayStart", None)
            limit = get_vars.get("pageLength", 0)
        else:
            start = get_vars.get("start", None)
            limit = get_vars.get("limit", 0)
        if limit:
            if limit.lower() == "none":
                limit = None
            else:
                try:
                    start = int(start)
                    limit = int(limit)
                except (ValueError, TypeError):
                    start = None
                    limit = 0 # use default
        else:
            # Use defaults
            start = None

        dtargs = attr.get("dtargs", {})

        if r.interactive:
            s3 = current.response.s3

            # How many records per page?
            if s3.dataTable_pageLength:
                display_length = s3.dataTable_pageLength
            else:
                display_length = widget.get("pagesize", 10)
            dtargs["dt_lengthMenu"] = [[10, 25, 50, -1],
                                       [10, 25, 50, str(current.T("All"))]
                                      ]

            # ORDERBY fallbacks: widget->resource->default
            orderby = widget.get("orderby")
            if not orderby:
                orderby = resource.get_config("orderby")
            if not orderby:
                orderby = default_orderby()

            # Server-side pagination?
            if not s3.no_sspag:
                dt_pagination = "true"
                if not limit and display_length is not None:
                    limit = 2 * display_length
                else:
                    limit = None
            else:
                dt_pagination = "false"

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

            if dt.empty:
                empty_str = self.crud_string(tablename,
                                             "msg_list_empty")
            else:
                empty_str = self.crud_string(tablename,
                                             "msg_no_match")
            empty = DIV(empty_str, _class="empty")

            dtargs["dt_pagination"] = dt_pagination
            dtargs["dt_pageLength"] = display_length
            # @todo: fix base URL (make configurable?) to fix export options
            s3.no_formats = True
            dtargs["dt_base_url"] = r.url(method="", vars={})
            dtargs["dt_ajax_url"] = r.url(vars={"update": widget["index"]},
                                            representation="aadata")
            actions = widget.get("actions")
            if callable(actions):
                actions = actions(r, list_id)
            if actions:
                dtargs["dt_row_actions"] = actions

            datatable = dt.html(totalrows,
                                displayrows,
                                id=list_id,
                                **dtargs)

            if dt.data:
                empty.update(_style="display:none")
            else:
                datatable.update(_style="display:none")
            contents = DIV(datatable, empty, _class="dt-contents")

            # Link for create-popup
            create_popup = self._create_popup(r,
                                              widget,
                                              list_id,
                                              resource,
                                              context,
                                              totalrows)

            # Card holder label and icon
            label = widget.get("label", "")
            # Activate if-required
            #if label and isinstance(label, basestring):
            if label:
                label = current.T(label)
            else:
                label = S3CRUD.crud_string(tablename, "title_list")
            icon = widget.get("icon", "")
            if icon:
                icon = ICON(icon)

            _class = self._lookup_class(r, widget)

            # Render the widget
            output = DIV(create_popup,
                         H4(icon, label,
                            _class="profile-sub-header"),
                         DIV(contents,
                             _class="card-holder"),
                         _class=_class)

            return output

        elif representation == "aadata":

            # Parse datatable filter/sort query
            searchq, orderby, left = resource.datatable_filter(list_fields,
                                                               get_vars)

            # ORDERBY fallbacks - datatable->widget->resource->default
            if not orderby:
                orderby = widget.get("orderby")
            if not orderby:
                orderby = resource.get_config("orderby")
            if not orderby:
                orderby = default_orderby()

            # DataTable filtering
            if searchq is not None:
                totalrows = resource.count()
                resource.add_filter(searchq)
            else:
                totalrows = None

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

            if totalrows is None:
                totalrows = displayrows

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

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

            return data

        else:
            # Really raise an exception here?
            r.error(415, current.ERROR.BAD_FORMAT)

    # -------------------------------------------------------------------------
dataset/ETHPy150Open sahana/eden/modules/s3/s3profile.py/S3Profile._datatable/original