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:
Simon Willison 2026-09-16 14:56:09 -07:00 • committed by GitHub
commit d2098e9f84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 2 deletions

View file

@ -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(

View file

@ -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(