filters_from_request plugin hook, now used in TableView

- New `filters_from_request` plugin hook, closes #473
- Used it to extract the logic from TableView that handles `_search` and
`_through` and `_where` - refs #1518

Also needed for this plugin work: https://github.com/simonw/datasette-leaflet-freedraw/issues/7
This commit is contained in:
Simon Willison 2021-12-17 11:02:14 -08:00 committed by GitHub
commit aa7f0037a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 354 additions and 112 deletions

View file

@ -1,4 +1,6 @@
from datasette.filters import Filters
from datasette.filters import Filters, through_filters, where_filters, search_filters
from datasette.utils.asgi import Request
from .fixtures import app_client
import pytest
@ -74,3 +76,86 @@ def test_build_where(args, expected_where, expected_params):
sql_bits, actual_params = f.build_where_clauses("table")
assert expected_where == sql_bits
assert {f"p{i}": param for i, param in enumerate(expected_params)} == actual_params
@pytest.mark.asyncio
async def test_through_filters_from_request(app_client):
request = Request.fake(
'/?_through={"table":"roadside_attraction_characteristics","column":"characteristic_id","value":"1"}'
)
filter_args = await (
through_filters(
request=request,
datasette=app_client.ds,
table="roadside_attractions",
database="fixtures",
)
)()
assert filter_args.where_clauses == [
"pk in (select attraction_id from roadside_attraction_characteristics where characteristic_id = :p0)"
]
assert filter_args.params == {"p0": "1"}
assert filter_args.human_descriptions == [
'roadside_attraction_characteristics.characteristic_id = "1"'
]
assert filter_args.extra_context == {}
@pytest.mark.asyncio
async def test_through_filters_from_request(app_client):
request = Request.fake(
'/?_through={"table":"roadside_attraction_characteristics","column":"characteristic_id","value":"1"}'
)
filter_args = await (
through_filters(
request=request,
datasette=app_client.ds,
table="roadside_attractions",
database="fixtures",
)
)()
assert filter_args.where_clauses == [
"pk in (select attraction_id from roadside_attraction_characteristics where characteristic_id = :p0)"
]
assert filter_args.params == {"p0": "1"}
assert filter_args.human_descriptions == [
'roadside_attraction_characteristics.characteristic_id = "1"'
]
assert filter_args.extra_context == {}
@pytest.mark.asyncio
async def test_where_filters_from_request(app_client):
request = Request.fake("/?_where=pk+>+3")
filter_args = await (
where_filters(
request=request,
datasette=app_client.ds,
database="fixtures",
)
)()
assert filter_args.where_clauses == ["pk > 3"]
assert filter_args.params == {}
assert filter_args.human_descriptions == []
assert filter_args.extra_context == {
"extra_wheres_for_ui": [{"text": "pk > 3", "remove_url": "/"}]
}
@pytest.mark.asyncio
async def test_search_filters_from_request(app_client):
request = Request.fake("/?_search=bobcat")
filter_args = await (
search_filters(
request=request,
datasette=app_client.ds,
database="fixtures",
table="searchable",
)
)()
assert filter_args.where_clauses == [
"rowid in (select rowid from searchable_fts where searchable_fts match escape_fts(:search))"
]
assert filter_args.params == {"search": "bobcat"}
assert filter_args.human_descriptions == ['search matches "bobcat"']
assert filter_args.extra_context == {"supports_search": True, "search": "bobcat"}

View file

@ -9,6 +9,7 @@ from .fixtures import (
from click.testing import CliRunner
from datasette.app import Datasette
from datasette import cli, hookimpl
from datasette.filters import FilterArguments
from datasette.plugins import get_plugins, DEFAULT_PLUGINS, pm
from datasette.utils.sqlite import sqlite3
from datasette.utils import CustomRow
@ -977,3 +978,20 @@ def test_hook_register_commands():
}
pm.unregister(name="verify")
importlib.reload(cli)
def test_hook_filters_from_request(app_client):
class ReturnNothingPlugin:
__name__ = "ReturnNothingPlugin"
@hookimpl
def filters_from_request(self, request):
if request.args.get("_nothing"):
return FilterArguments(["1 = 0"], human_descriptions=["NOTHING"])
pm.register(ReturnNothingPlugin(), name="ReturnNothingPlugin")
response = app_client.get("/fixtures/facetable?_nothing=1")
assert "0 rows\n where NOTHING" in response.text
json_response = app_client.get("/fixtures/facetable.json?_nothing=1")
assert json_response.json["rows"] == []
pm.unregister(name="ReturnNothingPlugin")