'Method to render the dataTable into html
@param totalrows: The total rows in the unfiltered query.
@param filteredrows: The total rows in the filtered query.
@param id: The id of the table these need to be unique if more
than one dataTable is to be rendered on the same page.
If this is not passed in then a unique id will be
generated. Regardless the id is stored in self.id
so it can be easily accessed after rendering.
@param draw: An unaltered copy of draw sent from the client used
by dataTables as a draw count.
@param attr: dictionary of attributes which can be passed in'

def html(self, totalrows, filteredrows, id=None, draw=1, **attr):

    flist = self.colnames
    if (not id):
        id = ('list_%s' % self.id_counter)
        self.id_counter += 1
    self.id = id
    bulkActions = attr.get('dt_bulk_actions', None)
    bulkCol = attr.get('dt_bulk_col', 0)
    if (bulkCol > len(flist)):
        bulkCol = len(flist)
    action_col = attr.get('dt_action_col', 0)
    if (action_col != 0):
        if ((action_col == (-1)) or (action_col >= len(flist))):
            action_col = (len(flist) - 1)
            attr['dt_action_col'] = action_col
        flist = ((flist[1:(action_col + 1)] + [flist[0]]) + flist[(action_col + 1):])
    if bulkActions:
        flist.insert(bulkCol, 'BULK')
        if (bulkCol <= action_col):
            action_col += 1
    pagination = (attr.get('dt_pagination', 'true') == 'true')
    if pagination:
        real_end = self.end
        self.end = (self.start + 1)
    table = self.table(id, flist, action_col)
    if pagination:
        self.end = real_end
        aadata = self.aadata(totalrows, filteredrows, id, draw, flist, action_col=action_col, stringify=False, **attr)
        cache = {'cacheLower': self.start, 'cacheUpper': (self.end if (filteredrows > self.end) else filteredrows), 'cacheLastJson': aadata}
    else:
        cache = None
    html = self.htmlConfig(table, id, self.orderby, self.rfields, cache, **attr)
    return html
