From 86240c7bf9b9e7001a2cb80ed25849d4b3ef8ed6 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Tue, 11 Aug 2026 11:11:44 -0500 Subject: [PATCH 1/7] Fix ad hoc query form action for #2487 --- datasette/templates/query.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datasette/templates/query.html b/datasette/templates/query.html index 8dd1037f..cdbed553 100644 --- a/datasette/templates/query.html +++ b/datasette/templates/query.html @@ -37,7 +37,7 @@ {% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} -
+

Custom SQL query{% if display_rows %} returning {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}{% endif %}{% if not query_error %} ({{ show_hide_text }}) {% endif %}

From 5fca43ddad90032dda3e03587b1acd201bd94c5a Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Tue, 11 Aug 2026 11:12:00 -0500 Subject: [PATCH 2/7] Add regression test for query form action --- tests/test_query_form_action.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/test_query_form_action.py diff --git a/tests/test_query_form_action.py b/tests/test_query_form_action.py new file mode 100644 index 00000000..fe8aaa34 --- /dev/null +++ b/tests/test_query_form_action.py @@ -0,0 +1,12 @@ +import pytest +from bs4 import BeautifulSoup as Soup + + +@pytest.mark.asyncio +async def test_ad_hoc_query_form_posts_to_query_route(ds_client): + response = await ds_client.get("/fixtures/-/query?sql=select+1") + assert response.status_code == 200 + + form = Soup(response.text, "html.parser").select_one("form.sql.core") + assert form is not None + assert form["action"] == "/fixtures/-/query" From f3135cf8fd6ccc0d9d7a1c701ab90d4d0b79caf5 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sun, 13 Sep 2026 07:49:12 -0500 Subject: [PATCH 3/7] Clarify ad hoc query form regression test name --- tests/test_query_form_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_query_form_action.py b/tests/test_query_form_action.py index fe8aaa34..fa4c2cbd 100644 --- a/tests/test_query_form_action.py +++ b/tests/test_query_form_action.py @@ -3,7 +3,7 @@ from bs4 import BeautifulSoup as Soup @pytest.mark.asyncio -async def test_ad_hoc_query_form_posts_to_query_route(ds_client): +async def test_ad_hoc_query_form_targets_query_route(ds_client): response = await ds_client.get("/fixtures/-/query?sql=select+1") assert response.status_code == 200 From b0da4f38ea97c564c92b2afbd7a778dabeafe4ba Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sun, 13 Sep 2026 07:49:27 -0500 Subject: [PATCH 4/7] Use URL helper for stored query form action --- datasette/templates/query.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datasette/templates/query.html b/datasette/templates/query.html index cdbed553..91bd7a2a 100644 --- a/datasette/templates/query.html +++ b/datasette/templates/query.html @@ -37,7 +37,7 @@ {% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} - +

Custom SQL query{% if display_rows %} returning {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}{% endif %}{% if not query_error %} ({{ show_hide_text }}) {% endif %}

From 6cd61bc0641fad1cac40243f83cdb8b807611c03 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sun, 13 Sep 2026 21:04:34 -0500 Subject: [PATCH 5/7] Test query form action with base_url --- tests/test_query_form_action.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_query_form_action.py b/tests/test_query_form_action.py index fa4c2cbd..c3af13cc 100644 --- a/tests/test_query_form_action.py +++ b/tests/test_query_form_action.py @@ -10,3 +10,12 @@ async def test_ad_hoc_query_form_targets_query_route(ds_client): form = Soup(response.text, "html.parser").select_one("form.sql.core") assert form is not None assert form["action"] == "/fixtures/-/query" + + +def test_ad_hoc_query_form_action_preserves_base_url(app_client_base_url_prefix): + response = app_client_base_url_prefix.get("/prefix/fixtures/-/query?sql=select+1") + assert response.status_code == 200 + + form = Soup(response.text, "html.parser").select_one("form.sql.core") + assert form is not None + assert form["action"] == "/prefix/fixtures/-/query" From 385536eb87739409be4e00bd3b7fb9827dd263d4 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Mon, 14 Sep 2026 03:59:36 -0500 Subject: [PATCH 6/7] test: cover encoded stored query form actions --- tests/test_query_form_action.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_query_form_action.py b/tests/test_query_form_action.py index c3af13cc..8eacca8e 100644 --- a/tests/test_query_form_action.py +++ b/tests/test_query_form_action.py @@ -19,3 +19,17 @@ def test_ad_hoc_query_form_action_preserves_base_url(app_client_base_url_prefix) form = Soup(response.text, "html.parser").select_one("form.sql.core") assert form is not None assert form["action"] == "/prefix/fixtures/-/query" + + +@pytest.mark.asyncio +async def test_stored_query_form_action_encodes_query_name(ds_client): + query_name = "query with spaces" + await ds_client.ds.update_query("fixtures", query_name, sql="select 1") + query_url = ds_client.ds.urls.query("fixtures", query_name) + + response = await ds_client.get(query_url) + assert response.status_code == 200 + + form = Soup(response.text, "html.parser").select_one("form.sql.core") + assert form is not None + assert form["action"] == query_url From d37284ac03a00a02cfdd2853af2fe5e4790bedb9 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Mon, 14 Sep 2026 06:03:21 -0500 Subject: [PATCH 7/7] test: cover stored query action with base_url --- tests/test_query_form_action.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_query_form_action.py b/tests/test_query_form_action.py index 8eacca8e..8f419d55 100644 --- a/tests/test_query_form_action.py +++ b/tests/test_query_form_action.py @@ -33,3 +33,21 @@ async def test_stored_query_form_action_encodes_query_name(ds_client): form = Soup(response.text, "html.parser").select_one("form.sql.core") assert form is not None assert form["action"] == query_url + + +@pytest.mark.asyncio +async def test_stored_query_form_action_preserves_base_url( + app_client_base_url_prefix, +): + query_name = "query with spaces" + await app_client_base_url_prefix.ds.update_query( + "fixtures", query_name, sql="select 1" + ) + query_url = app_client_base_url_prefix.ds.urls.query("fixtures", query_name) + + response = app_client_base_url_prefix.get(query_url) + assert response.status_code == 200 + + form = Soup(response.text, "html.parser").select_one("form.sql.core") + assert form is not None + assert form["action"] == query_url