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 `<a href="...">` 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
This commit is contained in:
Wes Turner 2026-03-02 22:40:39 -05:00
commit ba68e3a0a1

View file

@ -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(
'<a href="{table_path}/{flat_pks_quoted}">{flat_pks}</a>'.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