From 5116c4ec8aed5091e1f75415424b80f613518dc6 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 5 Aug 2018 12:59:45 -0700 Subject: [PATCH] WIP table_filter plugin hook --- datasette/hookspecs.py | 5 +++++ datasette/utils.py | 6 +++++- datasette/views/table.py | 17 +++++++++++++---- docs/plugins.rst | 25 +++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/datasette/hookspecs.py b/datasette/hookspecs.py index 233a9aa0..92343506 100644 --- a/datasette/hookspecs.py +++ b/datasette/hookspecs.py @@ -33,3 +33,8 @@ def publish_subcommand(publish): @hookspec(firstresult=True) def render_cell(value): "Customize rendering of HTML table cell values" + + +@hookspec +def table_filter(): + "Custom filtering of the current table based on the request" diff --git a/datasette/utils.py b/datasette/utils.py index 29360b35..1f2ed910 100644 --- a/datasette/utils.py +++ b/datasette/utils.py @@ -1,5 +1,5 @@ from contextlib import contextmanager -from collections import OrderedDict +from collections import OrderedDict, namedtuple import base64 import click import hashlib @@ -39,6 +39,10 @@ RUN apt-get update && \ ENV SQLITE_EXTENSIONS /usr/lib/x86_64-linux-gnu/mod_spatialite.so ''' +TableFilter = namedtuple("TableFilter", ( + "human_description_extras", "where_clauses", "params") +) + class InterruptedError(Exception): pass diff --git a/datasette/views/table.py b/datasette/views/table.py index ae71d33d..88b6748e 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -313,7 +313,7 @@ class TableView(RowTableShared): search_args = dict( pair for pair in special_args.items() if pair[0].startswith("_search") ) - search_descriptions = [] + human_description_extras = [] search = "" if fts_table and search_args: if "_search" in search_args: @@ -324,7 +324,7 @@ class TableView(RowTableShared): fts_table=escape_sqlite(fts_table), ) ) - search_descriptions.append('search matches "{}"'.format(search)) + human_description_extras.append('search matches "{}"'.format(search)) params["search"] = search else: # More complex: search against specific columns @@ -341,13 +341,22 @@ class TableView(RowTableShared): i=i ) ) - search_descriptions.append( + human_description_extras.append( 'search column "{}" matches "{}"'.format( search_col, search_text ) ) params["search_{}".format(i)] = search_text + # filter_arguments plugin hook support + for awaitable_fn in pm.hook.table_filter(): + extras = await awaitable_fn( + view=self, name=name, table=table, request=request + ) + human_description_extras.extend(extras.human_description_extras) + where_clauses.extend(extras.where_clauses) + params.update(extras.params) + table_rows_count = None sortable_columns = set() if not is_view: @@ -713,7 +722,7 @@ class TableView(RowTableShared): pass # human_description_en combines filters AND search, if provided - human_description_en = filters.human_description_en(extra=search_descriptions) + human_description_en = filters.human_description_en(extra=human_description_extras) if sort or sort_desc: sorted_by = "sorted by {}{}".format( diff --git a/docs/plugins.rst b/docs/plugins.rst index f69fed95..c7b3ee24 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -320,3 +320,28 @@ If the value matches that pattern, the plugin returns an HTML link element: href=jinja2.escape(data["href"]), label=jinja2.escape(data["label"] or "") or " " )) + +table_filter +~~~~~~~~~~~~ + +Apply additional SQL filters to the current table based on the request. + +This should return an awaitable function with the following signature: + +.. code-block:: python + + from datasette.utils import TableFilter + + @hookimpl + def table_filter(): + async def inner(view, name, table, request): + extra_human_descriptions = [] + where_clauses = [] + params = {} + # ... build those things here + return TableFilter( + human_description_extras=extra_human_descriptions, + where_clauses=where_clauses, + params=params, + ) + return inner