From ba68e3a0a1ac32397a0789a25996bc2f1a24963d Mon Sep 17 00:00:00 2001 From: Wes Turner Date: Mon, 2 Mar 2026 22:40:39 -0500 Subject: [PATCH] Apply `render_cell` plugin hook to primary key columns This update modifies the table view to execute the `render_cell` plugin hook against simple (single) primary key (or "Link") columns. Instead of always generating a default HTML anchor link pointing to the row view, Datasette now checks if any plugin returns a custom display value. If a custom rendering is provided, it is used; otherwise, it safely falls back to the default `` behavior. Key Changes & Optimizations: - Feature: Executes `render_cell` hooks for simple primary keys in `display_columns_and_rows`, safely resolving `pk_value` from the row. - Performance: Pre-computes `has_single_pk`, `pk_index`, and `has_render_cell_hooks` outside of the O(N) row loop to eliminate redundant operations. - Performance: Completely skips hook evaluation if no plugins are registered to listen to `render_cell`. - Refactor: Streamlines row cell appending by using an inline `A if A is not None else B` fallback, which prevents duplicate dictionary initializations and safely handles intentional empty strings (`""`) returned by plugins. - Compatibility: Passes `pks=pks_for_display` to the hook call to satisfy upstream API requirements. Assisted By: Gemini 3.1 Pro --- datasette/views/table.py | 42 +++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/datasette/views/table.py b/datasette/views/table.py index e1e5507f..d34b1952 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -195,6 +195,15 @@ async def display_columns_and_rows( for fk in await db.foreign_keys_for_table(table_name) } + has_single_pk = len(pks) == 1 + has_render_cell_hooks = bool(pm.hook.render_cell.get_hookimpls()) + pk_index = None + if has_single_pk: + for idx, c in enumerate(columns): + if c["name"] == pks[0]: + pk_index = idx + break + cell_rows = [] base_url = datasette.setting("base_url") for row in rows: @@ -202,15 +211,38 @@ async def display_columns_and_rows( # Unless we are a view, the first column is a link - either to the rowid # or to the simple or compound primary key if link_column: - is_special_link_column = len(pks) != 1 pk_path = path_from_row_pks(row, pks, not pks, False) + + # If there's a simple primary key, let plugins have a go + plugin_display_value = None + if has_single_pk and has_render_cell_hooks: + try: + pk_value = row[pks[0]] + except (IndexError, KeyError, TypeError): + pk_value = row[pk_index] if pk_index is not None else None + + for candidate in pm.hook.render_cell( + row=row, + value=pk_value, + column=pks[0], + table=table_name, + pks=pks_for_display, + database=database_name, + datasette=datasette, + request=request, + ): + candidate = await await_me_maybe(candidate) + if candidate is not None: + plugin_display_value = candidate + break + cells.append( { - "column": pks[0] if len(pks) == 1 else "Link", + "column": pks[0] if has_single_pk else "Link", "value_type": "pk", - "is_special_link_column": is_special_link_column, + "is_special_link_column": not has_single_pk, "raw": pk_path, - "value": markupsafe.Markup( + "value": plugin_display_value if plugin_display_value is not None else markupsafe.Markup( '{flat_pks}'.format( table_path=datasette.urls.table(database_name, table_name), flat_pks=str(markupsafe.escape(pk_path)), @@ -222,7 +254,7 @@ async def display_columns_and_rows( for value, column_dict in zip(row, columns): column = column_dict["name"] - if link_column and len(pks) == 1 and column == pks[0]: + if link_column and has_single_pk and column == pks[0]: # If there's a simple primary key, don't repeat the value as it's # already shown in the link column. continue