mirror of
https://github.com/simonw/datasette.git
synced 2026-09-18 06:24:18 +02:00
Fix facet selection for explicit exact filters
`colname__exact=value` is now treated the same as `colname=value` when determining which facets are selected. Closes #1695
This commit is contained in:
parent
266eaddb73
commit
d2098e9f84
4 changed files with 91 additions and 2 deletions
|
|
@ -264,10 +264,15 @@ class ColumnFacet(Facet):
|
|||
column_qs = column
|
||||
if column.startswith("_"):
|
||||
column_qs = f"{column}__exact"
|
||||
selected = (column_qs, str(row["value"])) in qs_pairs
|
||||
selected_args = {
|
||||
key: str(row["value"])
|
||||
for key in (column_qs, f"{column}__exact")
|
||||
if (key, str(row["value"])) in qs_pairs
|
||||
}
|
||||
selected = bool(selected_args)
|
||||
if selected:
|
||||
toggle_path = path_with_removed_args(
|
||||
self.request, {column_qs: str(row["value"])}
|
||||
self.request, selected_args
|
||||
)
|
||||
else:
|
||||
toggle_path = path_with_added_args(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Datasette plugins can now use **background tasks** to run code independent of th
|
|||
Bug fixes
|
||||
~~~~~~~~~
|
||||
|
||||
- Column facets now show the remove-filter link for filters using ``column__exact=value``, as well as ``column=value``. (:issue:`1695`)
|
||||
- The :ref:`alter-table API <TableAlterView>` now rolls back schema changes when a :ref:`write_wrapper <plugin_hook_write_wrapper>` raises after the write. (:issue:`2924`, :pr:`2925`)
|
||||
- The :ref:`extra_template_vars() <plugin_hook_extra_template_vars>` plugin hook can now return a function or awaitable that resolves to ``None`` when no extra variables are needed. (:issue:`2005`)
|
||||
- :ref:`request.headers <internals_request>` now supports case-insensitive header lookups, so ``request.headers.get("Content-Type")`` works as well as ``request.headers.get("content-type")``. (:issue:`1861`)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
from urllib.parse import parse_qsl, urlsplit
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -151,6 +152,63 @@ async def test_column_facet_results(ds_client):
|
|||
] == buckets
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"column,filters,remaining",
|
||||
[
|
||||
("COUNTY", "COUNTY=Lee", []),
|
||||
("COUNTY", "COUNTY__exact=Lee", []),
|
||||
("COUNTY", "COUNTY=Lee&COUNTY__exact=Lee", []),
|
||||
("COUNTY", "COUNTY__exact=Lee&COUNTY__exact=Lee", []),
|
||||
(
|
||||
"COUNTY",
|
||||
"COUNTY__exact=Lee&COUNTY__exact=Polk",
|
||||
[("COUNTY__exact", "Polk")],
|
||||
),
|
||||
("_county", "_county__exact=Lee", []),
|
||||
("_county", "_county__exact=Lee&_county=Lee", [("_county", "Lee")]),
|
||||
],
|
||||
)
|
||||
async def test_column_facet_selected_exact_filters(
|
||||
ds_client, column, filters, remaining
|
||||
):
|
||||
facet = ColumnFacet(
|
||||
ds_client.ds,
|
||||
Request.fake(f"/?_facet={column}&{filters}&other=keep&_sort={column}"),
|
||||
database="fixtures",
|
||||
sql=f"select 'Lee' as {column}",
|
||||
)
|
||||
buckets, timed_out = await facet.facet_results()
|
||||
assert not timed_out
|
||||
result = buckets[0]["results"][0]
|
||||
assert result["selected"] is True
|
||||
assert parse_qsl(urlsplit(result["toggle_url"]).query) == [
|
||||
("_facet", column),
|
||||
*remaining,
|
||||
("other", "keep"),
|
||||
("_sort", column),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_column_facet_underscore_argument_is_not_a_filter(ds_client):
|
||||
facet = ColumnFacet(
|
||||
ds_client.ds,
|
||||
Request.fake("/?_facet=_county&_county=Lee"),
|
||||
database="fixtures",
|
||||
sql="select 'Lee' as _county",
|
||||
)
|
||||
buckets, timed_out = await facet.facet_results()
|
||||
assert not timed_out
|
||||
result = buckets[0]["results"][0]
|
||||
assert result["selected"] is False
|
||||
assert parse_qsl(urlsplit(result["toggle_url"]).query) == [
|
||||
("_facet", "_county"),
|
||||
("_county", "Lee"),
|
||||
("_county__exact", "Lee"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_column_facet_results_column_starts_with_underscore(ds_client):
|
||||
facet = ColumnFacet(
|
||||
|
|
|
|||
|
|
@ -484,6 +484,31 @@ async def test_facet_display(ds_client):
|
|||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"filters", ["state=CA", "state__exact=CA", "state=CA&state__exact=CA"]
|
||||
)
|
||||
async def test_facet_remove_exact_filter(ds_client, filters):
|
||||
response = await ds_client.get(
|
||||
f"/fixtures/facetable?_facet=state&{filters}&on_earth=1"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
soup = Soup(response.text, "html.parser")
|
||||
remove_link = soup.select_one('.facet-info[data-column="state"] li a.cross')
|
||||
assert remove_link is not None
|
||||
url = urllib.parse.urlsplit(remove_link["href"])
|
||||
assert urllib.parse.parse_qsl(url.query) == [
|
||||
("_facet", "state"),
|
||||
("on_earth", "1"),
|
||||
]
|
||||
unfiltered = await ds_client.get(f"{url.path}.json?{url.query}")
|
||||
assert unfiltered.status_code == 200
|
||||
rows = unfiltered.json()["rows"]
|
||||
assert len(rows) == 14
|
||||
assert all(row["on_earth"] == 1 for row in rows)
|
||||
assert {row["state"] for row in rows} == {"CA", "MI"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_facets_persist_through_filter_form(ds_client):
|
||||
response = await ds_client.get(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue