Tracking Column Modifications in Grist – Is It Possible?

Hi everyone,

I’m exploring ways to implement a custom widget or alternative solution in Grist that tracks modifications in certain columns. Specifically, my goal is to have a mechanism where, when any of a set of columns is modified, a dedicated “UpdateTracker” column is automatically updated with the name of the column that changed.

For example, consider a table with these columns:

  • Name
  • UserName
  • Date
  • Email
  • Phone

Currently, the only solution I’ve found is to create an associated tracking column for each of these fields, so that each time one is updated, its corresponding tracker is updated with the column name (e.g., “Name” when the Name column is modified). However, this approach quickly multiplies the number of columns and clutters the table schema.

My question is:
Is it possible to create a custom widget—or use another mechanism (maybe leveraging Grist’s internal trigger system such as _grist_Triggers)—that can monitor these changes and update a single tracking column (or otherwise log the change) without needing to create separate tracking columns for each field?

I would appreciate any insights, sample code, or references to documentation that could help implement a more elegant solution.

Thanks in advance!

1 Like

AFAIK you could just create a dependency on the _grist_Tables_column meta table. For example, this formula: _grist_Tables_column.lookupRecords(tableId="Test") gets updated as it should when you create a column on the ‘Test’ table.

Hi,

For now I use the following formula which is triggered upon modification of the Last Modified automatic column. It joins multiple columns content in one single log.

import datetime
log=" ".join(f'[{w}]' for w in [$A, $B, $C])
ticket=Change_Identification.lookupOne(User=user.Name).Change_Identifier
current_time = NOW().strftime("%Y-%m-%d %H:%M:%S")
new_entry = f"{ticket}: ({user.Name}) {current_time} - {log}"
return (value + "\n" + new_entry) if value else new_entry

It looks like this but you can adapt as you like:

You’ll see I use a Change_Identification table to justify changes through a change ticket. My dream would be to make it mandatory before allowing any change with an auto-delete of the ticket identifier upon user logout…

However, for change tracking I think a better alternative would be to use the grist internal history but this is not possible for now.

Thank you for the nice code!

I am sharing here a revised version for my needs, which only logs the changes from the same user if they are more than 60 min apart. It avoids cluttering the log with a user actively editing a record:

import datetime

current_time = datetime.datetime.now()
current_time_str = current_time.strftime("%Y-%m-%d %H:%M:%S")
new_entry = f"{user.Email} - {current_time_str}"

# Try to extract the last line from the existing field
last_line = value.strip().split("\n")[-1]

try:
    last_name, last_time_str = last_line.rsplit(" - ", 1)
    last_time = datetime.datetime.strptime(last_time_str, "%Y-%m-%d %H:%M:%S")
    time_diff = (current_time - last_time).total_seconds() / 60  # in minutes

    # If same user and less than 60 minutes: do not add anything
    if last_name == user.Email and time_diff < 60:
        return value  # return the existing value unchanged

except Exception:
    pass  # If parsing fails, ignore and continue normally

# If the cell is empty, simply add the first entry
if not value:
    return new_entry

# If everything is fine: append the new entry
return value + "\n" + new_entry

Disclaimer: the code is AI-generated and shared after some testing that showed it works as intended!

1 Like