Changing the database table with editable dash datatable functionality

Here is a clientside callback that I use:

It builds a secondary table with the changes, using the Key (unique identifier column, identifier). Shows the column that was changed and the oldvalue and the newvalue:

app.clientside_callback(
    """function addToHistory(ts, data, data_previous, diff_store_data, keys) {
    if (ts) {
        let difference = data.filter(x => !data_previous.includes(x))[0]
        let old = data_previous.filter(x => !data.includes(x))[0]
        newKeys = Object.keys(difference)
        keys = keys.toLowerCase().split('|')
        info = ''
        oldValue = ''
        newValue = ''
        column = ''
        for (x=0; x<keys.length; x++) {
            for (y=0; y<newKeys.length; y++) {
                if (newKeys[y].toLowerCase() == keys[x]) {
                    newData = String(difference[newKeys[y]])
                    if (newData.includes('[')) {
                        newData = newData.split(']')[0].split('[')[1]
                    }
                    info += newData + '|'
                }
                if (difference[newKeys[y]] != old[newKeys[y]]) {
                    column = newKeys[y]
                    oldValue = old[newKeys[y]]
                    newValue = difference[newKeys[y]]
                }
            }
        }
        info = info.substring(0,info.length-1)
        diff_store_data.unshift({"Key":info, "Column":column, "OldValue":oldValue, "NewValue":newValue})
        return diff_store_data
    }}""",
    Output("history", "data"),
    [Input("information", "data_timestamp")],
    [
        State("information", "data"),
        State("information", "data_previous"),
        State("history", "data"),
        State('keys', 'value'),
    ], prevent_initial_call=True
)

Then on “submit”, you roll all the changes up to their key and submit the changes to the backend.

Also, I have another clientside callback which uses the key to undo a change upon deletion of the record:

app.clientside_callback(
    """function reloadHistory(ts, data, data_previous, history_data, keys) {
    if (ts && data.length > 0) {
        let difference = data_previous.filter(x => !history_data.includes(x))[0]
        newKeys = Object.keys(data[0])
        oldKeys = keys.split('|')
        for (x=0; x<data.length; x++) {
            test = false
            for (y=0; y<oldKeys.length; y++) {
                for (z=0; z<newKeys.length; z++) {
                    if (newKeys[z].toLowerCase() == oldKeys[y]) {
                        newData = String(data[x][newKeys[z]])
                        try {
                        if (newData.includes('[')) {
                            newData = newData.split(']')[0].split('[')[1]
                        } } catch {}
                        if (String(newData) == difference['Key'].split('|')[y]) {
                            test = true
                        } else {
                            test = false
                            break
                        }
                    }
                }
                if (!test) {
                    break
                }
            }
            if (test) {
                data[x][difference['Column']] = difference['OldValue']
                break
            }
        }
        return data
    }}""",
    Output("information", "data"),
    Input("history", "data_timestamp"),
    [State('information', 'data'),
     State('history', 'data_previous'),
     State('history', 'data'),
     State('keys', 'value')], prevent_initial_call=True
)

Please note, I have my keys delimited by ‘|’ for ease, you could substitute with a list or just one for something where there is only one identifier.

I allow for multiple keys in the event that I dont want to use a record uid, but instead of combination of two columns.

2 Likes