Haha, whoops.
Depends on the amount of data that you build.
Try this version, its similar to yours, except that it stores the changes on the server using your session as a key:
app.py
import dash_ag_grid as dag
from dash import Dash, Input, Output, html, dcc, State, no_update
import requests, json
import flask
import pandas as pd
import uuid
app = Dash(__name__)
server = app.server
server.secret_key = "xyz"
alterations = {}
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
df.reset_index(inplace=True)
df.rename(columns={"index": "InputRowId"}, inplace=True)
rowData = df.to_dict('records')
columnDefs = [
# Row group by country and by year is enabled.
{"field": "country", "sortable": True, "filter": True, "rowGroup": True, "hide": True},
{"field": "year", "sortable": True, "filter": True, "rowGroup": True, "hide": True},
{"field": "athlete", "sortable": True, "filter": True},
{"field": "age", "sortable": True, "filter": True},
{"field": "date", "sortable": True, "filter": True},
{"field": "sport", "sortable": True, "filter": True, 'editable': True},
{"field": "total", "sortable": True, "filter": True, "aggFunc": "sum"},
{"field": "InputRowId", "sortable": True, "filter": True, 'hide': True, 'suppressColumnsToolPanel': True},
]
def extractRowsFromData(request, df):
response = []
## create session uid if not already done
if not flask.session.get('uid'):
flask.session['uid'] = uuid.uuid4()
## pull session stored changes
if not alterations.get(flask.session['uid']):
alterations[flask.session['uid']] = {}
myalterations = alterations.get(flask.session['uid'])
groupBy = []
dff = df.copy()
for k in myalterations.keys():
for key in myalterations[k].keys():
dff.at[k, key] = myalterations[k][key]
if request['rowGroupCols']:
groupBy = [i['id'] for i in request['rowGroupCols']]
agg = {}
if request['valueCols']:
agg = {i['id']: i['aggFunc'] for i in request['valueCols']}
if not request['groupKeys']:
if groupBy:
if agg:
dff = dff.groupby(groupBy[0]).agg(agg).reset_index()
else:
dff = dff.groupby(groupBy[0]).agg('count').reset_index()
else:
for i in range(len(request['groupKeys'])):
dff = dff[dff[request['rowGroupCols'][i]['id']] == request['groupKeys'][i]]
if len(request['groupKeys']) != len(groupBy):
if agg:
dff = dff.groupby(groupBy[:len(request['groupKeys']) + 1]).agg(agg).reset_index()
else:
dff = dff.groupby(groupBy[:len(request['groupKeys']) + 1]).agg('count').reset_index()
dff = dff.sort_values(by=[i['colId'] for i in request['sortModel'] if i['colId'] in dff.columns],
ascending=[i['sort'] == 'asc' for i in request['sortModel'] if i['colId'] in dff.columns])
return {'rowData': dff.to_dict('records')[request['startRow']: request['endRow']], 'rowCount': len(dff)}
@server.route('/api/serverData', methods=['POST'])
def serverData():
response = extractRowsFromData(flask.request.json, df)
return json.dumps(response)
grid = html.Div(
[
dag.AgGrid(
id="grid",
columnDefs=columnDefs,
dashGridOptions={"rowSelection": "multiple", "sideBar": True},
defaultColDef=dict(
resizable=True,
enableRowGroup=True,
enableValue=True,
enablePivot=True
),
enableEnterpriseModules=True,
rowModelType="serverSide",
style={'overflow': 'auto', 'resize': 'both'}
),
]
)
app.layout = html.Div(
[
dcc.Markdown("Example: Organisational Hierarchy using Tree Data "),
grid,
html.Div(id='hidden-div', style={'display': 'hidden'}),
dcc.Store(id='stored_changes', data={}, storage_type='local')
]
)
app.clientside_callback(
"""async function (id) {
const updateData = (grid) => {
var datasource = createServerSideDatasource();
grid.setServerSideDatasource(datasource);
};
var grid;
grid = await window.dash_ag_grid.getApiAsync(id)
if (grid) {
updateData(grid)
}
return window.dash_clientside.no_update
}""",
Output('grid', 'id'), Input('grid', 'id')
)
@app.callback(Output('stored_changes', 'data', allow_duplicate=True),
Input('grid', 'cellValueChanged'),
prevent_initial_call=True)
def syncData(changes):
## load and update alterations
if not alterations.get(flask.session['uid']):
alterations[flask.session['uid']] = {}
myalterations = alterations.get(flask.session['uid'])
for c in changes:
if not myalterations.get(c['data']['InputRowId']):
myalterations[c['data']['InputRowId']] = {}
myalterations[c['data']['InputRowId']][c['colId']] = c['value']
return no_update
if __name__ == "__main__":
app.run(debug=True)
js
async function getServerData(request) {
response = await fetch('./api/serverData', {'method': 'POST', 'body': JSON.stringify(request),
'headers': {'content-type': 'application/json'}})
return response.json()
}
function createServerSideDatasource() {
const dataSource = {
getRows: async (params) => {
var result = await getServerData(params.request)
setTimeout(function () {
params.success(result);
}, 200);
},
};
return dataSource;
}
I added a couple of fixes, your version would have only stored one column per key, this one will store all the changes on the df.
I also am making sure that the sorting columns exist in the data, as this was causing some error.