From 865f35ff10e03e8596b0267a9e49c05949fd4115 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 23 May 2026 09:10:25 -0700 Subject: [PATCH] Move default Jump items to datasette.default_jump_items plugin --- datasette/default_jump_items.py | 119 ++++++++++++++++++++++++++++++++ datasette/plugins.py | 1 + datasette/views/special.py | 116 +------------------------------ 3 files changed, 122 insertions(+), 114 deletions(-) create mode 100644 datasette/default_jump_items.py diff --git a/datasette/default_jump_items.py b/datasette/default_jump_items.py new file mode 100644 index 00000000..74d8a87f --- /dev/null +++ b/datasette/default_jump_items.py @@ -0,0 +1,119 @@ +from datasette import hookimpl +from datasette.jump import JumpSQL + + +async def _query_display_names_sql(datasette, actor): + selects = [] + params = {} + for database_name in datasette.databases.keys(): + queries = await datasette.get_canned_queries(database_name, actor) + for query_name, query in queries.items(): + display_name = query.get("title") if isinstance(query, dict) else None + if not display_name: + continue + index = len(selects) + params[f"display_database_{index}"] = database_name + params[f"display_query_{index}"] = query_name + params[f"display_name_{index}"] = str(display_name) + selects.append(f""" + SELECT + :display_database_{index} AS database_name, + :display_query_{index} AS query_name, + :display_name_{index} AS display_name + """) + if not selects: + return ( + "SELECT NULL AS database_name, NULL AS query_name, NULL AS display_name WHERE 0", + {}, + ) + return " UNION ALL ".join(selects), params + + +@hookimpl +def jump_items_sql(datasette, actor, request): + async def inner(): + database_sql, database_params = await datasette.allowed_resources_sql( + action="view-database", actor=actor + ) + table_sql, table_params = await datasette.allowed_resources_sql( + action="view-table", actor=actor + ) + query_sql, query_params = await datasette.allowed_resources_sql( + action="view-query", actor=actor + ) + query_display_names_sql, query_display_names_params = ( + await _query_display_names_sql(datasette, actor) + ) + return [ + JumpSQL( + sql=f""" + WITH allowed_databases AS ( + {database_sql} + ) + SELECT + 'database' AS type, + parent AS label, + 'Database' AS description, + NULL AS url, + parent AS database_name, + NULL AS resource_name, + parent AS search_text, + 10 AS sort_key, + 'datasette' AS source, + NULL AS display_name + FROM allowed_databases + """, + params=database_params, + ), + JumpSQL( + sql=f""" + WITH allowed_tables AS ( + {table_sql} + ) + SELECT + CASE WHEN catalog_views.view_name IS NULL THEN 'table' ELSE 'view' END AS type, + allowed_tables.parent || ': ' || allowed_tables.child AS label, + CASE WHEN catalog_views.view_name IS NULL THEN 'Table' ELSE 'View' END AS description, + NULL AS url, + allowed_tables.parent AS database_name, + allowed_tables.child AS resource_name, + allowed_tables.parent || ' ' || allowed_tables.child AS search_text, + CASE WHEN catalog_views.view_name IS NULL THEN 20 ELSE 25 END AS sort_key, + 'datasette' AS source, + NULL AS display_name + FROM allowed_tables + LEFT JOIN catalog_views + ON catalog_views.database_name = allowed_tables.parent + AND catalog_views.view_name = allowed_tables.child + """, + params=table_params, + ), + JumpSQL( + sql=f""" + WITH allowed_queries AS ( + {query_sql} + ), + query_display_names AS ( + {query_display_names_sql} + ) + SELECT + 'query' AS type, + allowed_queries.parent || ': ' || allowed_queries.child AS label, + 'Canned query' AS description, + NULL AS url, + allowed_queries.parent AS database_name, + allowed_queries.child AS resource_name, + allowed_queries.parent || ' ' || allowed_queries.child || ' ' || COALESCE(query_display_names.display_name, '') AS search_text, + 30 AS sort_key, + 'datasette' AS source, + query_display_names.display_name AS display_name + FROM allowed_queries + LEFT JOIN query_display_names + ON query_display_names.database_name = allowed_queries.parent + AND query_display_names.query_name = allowed_queries.child + """, + params={**query_params, **query_display_names_params}, + ), + ] + + return inner diff --git a/datasette/plugins.py b/datasette/plugins.py index b01b386c..15041e59 100644 --- a/datasette/plugins.py +++ b/datasette/plugins.py @@ -29,6 +29,7 @@ DEFAULT_PLUGINS = ( "datasette.default_magic_parameters", "datasette.blob_renderer", "datasette.default_menu_links", + "datasette.default_jump_items", "datasette.handle_exception", "datasette.forbidden", "datasette.events", diff --git a/datasette/views/special.py b/datasette/views/special.py index cb6524dc..2022e4a7 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -921,118 +921,7 @@ class JumpView(BaseView): name = "jump" has_json_alternate = False - async def _query_display_names_sql(self, request): - selects = [] - params = {} - for database_name in self.ds.databases.keys(): - queries = await self.ds.get_canned_queries(database_name, request.actor) - for query_name, query in queries.items(): - display_name = query.get("title") if isinstance(query, dict) else None - if not display_name: - continue - index = len(selects) - params[f"display_database_{index}"] = database_name - params[f"display_query_{index}"] = query_name - params[f"display_name_{index}"] = str(display_name) - selects.append(f""" - SELECT - :display_database_{index} AS database_name, - :display_query_{index} AS query_name, - :display_name_{index} AS display_name - """) - if not selects: - return ( - "SELECT NULL AS database_name, NULL AS query_name, NULL AS display_name WHERE 0", - {}, - ) - return " UNION ALL ".join(selects), params - - async def _core_fragments(self, request): - database_sql, database_params = await self.ds.allowed_resources_sql( - action="view-database", actor=request.actor - ) - table_sql, table_params = await self.ds.allowed_resources_sql( - action="view-table", actor=request.actor - ) - query_sql, query_params = await self.ds.allowed_resources_sql( - action="view-query", actor=request.actor - ) - query_display_names_sql, query_display_names_params = ( - await self._query_display_names_sql(request) - ) - return [ - JumpSQL( - sql=f""" - WITH allowed_databases AS ( - {database_sql} - ) - SELECT - 'database' AS type, - parent AS label, - 'Database' AS description, - NULL AS url, - parent AS database_name, - NULL AS resource_name, - parent AS search_text, - 10 AS sort_key, - 'datasette' AS source, - NULL AS display_name - FROM allowed_databases - """, - params=database_params, - ), - JumpSQL( - sql=f""" - WITH allowed_tables AS ( - {table_sql} - ) - SELECT - CASE WHEN catalog_views.view_name IS NULL THEN 'table' ELSE 'view' END AS type, - allowed_tables.parent || ': ' || allowed_tables.child AS label, - CASE WHEN catalog_views.view_name IS NULL THEN 'Table' ELSE 'View' END AS description, - NULL AS url, - allowed_tables.parent AS database_name, - allowed_tables.child AS resource_name, - allowed_tables.parent || ' ' || allowed_tables.child AS search_text, - CASE WHEN catalog_views.view_name IS NULL THEN 20 ELSE 25 END AS sort_key, - 'datasette' AS source, - NULL AS display_name - FROM allowed_tables - LEFT JOIN catalog_views - ON catalog_views.database_name = allowed_tables.parent - AND catalog_views.view_name = allowed_tables.child - """, - params=table_params, - ), - JumpSQL( - sql=f""" - WITH allowed_queries AS ( - {query_sql} - ), - query_display_names AS ( - {query_display_names_sql} - ) - SELECT - 'query' AS type, - allowed_queries.parent || ': ' || allowed_queries.child AS label, - 'Canned query' AS description, - NULL AS url, - allowed_queries.parent AS database_name, - allowed_queries.child AS resource_name, - allowed_queries.parent || ' ' || allowed_queries.child || ' ' || COALESCE(query_display_names.display_name, '') AS search_text, - 30 AS sort_key, - 'datasette' AS source, - query_display_names.display_name AS display_name - FROM allowed_queries - LEFT JOIN query_display_names - ON query_display_names.database_name = allowed_queries.parent - AND query_display_names.query_name = allowed_queries.child - """, - params={**query_params, **query_display_names_params}, - ), - ] - - async def _plugin_fragments(self, request): + async def _fragments(self, request): fragments = [] for hook in pm.hook.jump_items_sql( datasette=self.ds, @@ -1070,8 +959,7 @@ class JumpView(BaseView): q = request.args.get("q", "").strip() terms = q.split() pattern = "%" + "%".join(terms) + "%" if terms else "%" - fragments = await self._core_fragments(request) - fragments.extend(await self._plugin_fragments(request)) + fragments = await self._fragments(request) union_parts = [] all_params = {"q": q, "pattern": pattern}