Can a dash data table native sort keep the fixed rows at the top?

When I sort, the fixed rows get sorted. Is there a property somewhere to govern that behavior?

Hi @jim79lynn ! I think so. You could use a combination of sort_action = 'custom' and a sorting callback, as explained in the documentation:

sort_action ( a value equal to: ‘custom’, ‘native’ or ‘none’ ; default 'none' ): The sort_action property enables data to be sorted on a per-column basis. If 'none' , then the sorting UI is not displayed. If 'native' , then the sorting UI is displayed and the sorting logic is handled by the table. That is, it is performed on the data that exists in the data property. If 'custom' , the the sorting UI is displayed but it is the responsibility of the developer to program the sorting through a callback (where sort_by would be the input and data would be the output). Clicking on the sort arrows will update the sort_by property.

sort_by ( list of dicts ; optional): sort_by describes the current state of the sorting UI. That is, if the user clicked on the sort arrow of a column, then this property will be updated with the column ID and the direction ( asc or desc ) of the sort. For multi-column sorting, this will be a list of sorting parameters, in the order in which they were clicked.

sort_by is a list of dicts with keys:

  • column_id ( string ; required)
  • direction ( a value equal to: ‘asc’ or ‘desc’ ; required)

To learn how to write sorting callbacks you can check this doc.

Here you have an example that can be used as a template:

import dash
from dash import Dash, dash_table, dcc, html
from dash.dependencies import Input, Output
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')

# number of rows that we want to fix
n_fixed = 4

# auxiliar value that will be treated as null in the id column. 
# You can change it but you should be careful that it isn't a value that appears in other columns (because it would be trearted as null in those too)
sort_as_null = 'sort_this_row'

# add an id column and set it as the index. Later, we will hide it so that it isn't shown in the app
# At this point the data must be already ordered (the first rows must be the ones that you want to fix)
# we asign a sequential index for the fixed columns and the null (sort_as_null) value for the rest
df['id'] = [str(i) if i <= n_fixed else sort_as_null for i in range(1,len(df)+1)]

app = Dash(__name__)

app.layout = html.Div([
    dash_table.DataTable(
        id='datatable-interactivity',
        columns=[
            {"name": i, "id": i, "deletable": True, "selectable": True} for i in df.columns
        ],
        data=df.to_dict('records'),
        editable=True,
        # here we specify the number of rows to be fixed (n_fixed) and/or the headers. Default { headers: False, data: 0}
        fixed_rows = { 'headers': True, 'data': n_fixed}, 
        # here we have the important arguments (sort_action and sort_mode) which must be set to 'custom' and 'multi'
        sort_action="custom",
        sort_mode="multi",
        sort_as_null = [sort_as_null],
        # hide id column
        hidden_columns = ['id']
    )
])

@app.callback(
    Output('datatable-interactivity', 'data'),
    Input('datatable-interactivity', 'sort_by')
)
def sorting(sort):
    if sort is None :
        raise dash.exceptions.PreventUpdate
    else :
        # what we do here is put the id column at the beginning of the sort_by argument
        # the result is that our data gets ordered FIRST by the id and then by the other columns that the user specifies
        default_order = [{'column_id':'id', 'direction':'asc'}]
        if type(sort) == dict : sort = [sort]
        sort = default_order + sort
        
        dff = df.sort_values(
            by = [s['column_id'] for s in sort],
            ascending=[s['direction'] for s in sort],
            inplace=False
        )
        
    return dff.to_dict('records')

if __name__ == '__main__':
    app.run_server(debug=True, use_reloader = False)

Hope this helps! :four_leaf_clover:

1 Like