Hi!
Does no_update or exceptions.PreventUpdate works for DataTable ?
I need to block changes in some cells in editable table by some criteria (cells, rows or values).
Or it can be solved only with JavaScript hardcoding?
Hi!
Does no_update or exceptions.PreventUpdate works for DataTable ?
I need to block changes in some cells in editable table by some criteria (cells, rows or values).
Or it can be solved only with JavaScript hardcoding?
Hey Viktor (@texikator ),
welcome to the community,
As far as I know they are not necessarily connected, but I’m happy to look more into it if you provide a minimal reproducible example.
The dash.no_update means that, although the callback is triggered, it will not update certain callback outputs. Therefore, if the data
property of the DataTable is a callback output, and you return dash.no_update
, that DataTable should not be updated.
Thank you, adamschroeder !
I tried to use this code.
PreventUpdate and no_update does not work.
from dash import Dash, Input, Output, callback, dash_table, html, callback_context, dcc, State, no_update
app = Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='editing-prune-data',
columns=[{
'name': 'Column {}'.format(i),
'id': 'column-{}'.format(i)
} for i in range(1, 5)],
data=[
{'column-{}'.format(i): (j + (i-1)*5) for i in range(1, 5)}
for j in range(5)
],
editable=True
),
html.Div(id='editing-prune-data-output')
])
@app.callback(Output('editing-prune-data', 'data'),
Input('editing-prune-data', 'data'))
def display_output(rows):
# raise dash.exceptions.PreventUpdate
return no_update
if __name__ == '__main__':
app.run_server(debug=True)
Where is a mistake there …
My dash version is 2.6.1
Hi @texikator,
Here’s an example (extended from your code) of “locking” edits made on a specific column by returning the previous table state. You can take this further by locking down edits made on rows, or edits made on specific values as well. See the DataTable reference for more information, specifically active_cell
, selected_cells
, start_cell
, and end_cell
.
from dash import Dash, Input, Output, callback, dash_table, html, callback_context, dcc, State, no_update
app = Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='editing-prune-data',
columns=[{
'name': 'Column {}'.format(i),
'id': 'column-{}'.format(i)
} for i in range(1, 5)],
data=[
{'column-{}'.format(i): (j + (i - 1) * 5) for i in range(1, 5)}
for j in range(5)
],
editable=True
),
html.Div(id='editing-prune-data-output')
])
@app.callback(
Output('editing-prune-data', 'data'),
Input('editing-prune-data', 'data'),
[State('editing-prune-data', 'data_previous'),
State('editing-prune-data', 'active_cell'), ],
prevent_initial_call=True
)
def display_output(new_rows, previous_rows, selected_cell):
# if we're editing column 2, revert any changes
if selected_cell['column_id'] == 'column-2':
return previous_rows
else:
return new_rows
if __name__ == '__main__':
app.run_server(debug=True)
Thank you, [3d65](Profile - 3d65 - Plotly Community Forum !
It works! but ai see a some strange thing: if i navigate in this column via UP and DOWN keys, or if i change cell via mouse click - it works fine, but when i use LEFT and RIGHT keys - it’s works partially, some times in another columns …
That’s pretty interesting; I assume this is due to a table data update not being triggered until focusing a new row, as editing data on a record (row) basis is more common and expected.
If you want to keep it simple and lock down columns, you can follow the built in functionality, specifying editable=False
in the table definition on columns you want to lock. Here’s an example where a column is locked, and styled appropriately.
from dash import Dash, dash_table, html
app = Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='editing-prune-data',
columns=[{
'name': 'Column {}'.format(i),
'id': 'column-{}'.format(i),
'editable': False if i == 3 else True
} for i in range(1, 5)],
data=[
{'column-{}'.format(i): (j + (i - 1) * 5) for i in range(1, 5)}
for j in range(5)
],
style_data_conditional=[{
'if': {'column_editable': False},
'backgroundColor': '#9c9c9c',
'border': '1px solid #5f5f5f',
'color': '#535353'
}],
style_header_conditional=[{
'if': {'column_editable': False},
'backgroundColor': '#9c9c9c',
'border': '1px solid #5f5f5f',
'color': '#535353'
}],
editable=True
),
html.Div(id='editing-prune-data-output')
])
if __name__ == '__main__':
app.run_server(debug=True)
No, settings on culumn-level isn’t variant in my case.
Hi @texikator
I think @3d65 is on the right track. You could try comparing just the data
and data_previous
If any of the changes are in the blocked fields you could return the data_previous
. Then you wouldn’t need the active_cell
prop.
One thing to add based on my experience, data_timestamp will be triggered when a cell value has changed. Instead of actice_cell, use data_timestamp as the input. Then you know the data has been modified, not just clicked on.
For what it’s worth.
@texikator
Following up, here’s a working example of how to lock rows and columns using what @AnnMarieW mentioned.
from dash import Dash, Input, Output, callback, dash_table, html, callback_context, dcc, State, no_update
app = Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='editing-prune-data',
columns=[{
'name': 'Column {}'.format(i),
'id': 'column-{}'.format(i)
} for i in range(1, 5)],
data=[
{'column-{}'.format(i): (j + (i - 1) * 5) for i in range(1, 5)}
for j in range(5)
],
editable=True
),
html.Div(id='editing-prune-data-output')
])
@app.callback(
Output('editing-prune-data', 'data'),
Input('editing-prune-data', 'data'),
[State('editing-prune-data', 'data_previous')],
prevent_initial_call=True
)
def display_output(new_rows, prev_rows):
# check if every row in target_col is the same
target_col = 'column-2'
col_no_change = [prev[target_col] == new[target_col] for prev, new in zip(prev_rows, new_rows)]
# check if every column in row 0 is the same
target_row = 0
row_no_change = [prev == new for prev, new in zip(prev_rows[target_row].values(), new_rows[target_row].values())]
# if we haven't changed anything in target_col or target_row, proceed with the update
if all(col_no_change) and all(row_no_change):
return new_rows
return prev_rows
if __name__ == '__main__':
app.run_server(debug=True)