From f7e9dbc27efe31968a9d673322c5a86a45b93ef4 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Tue, 26 May 2026 14:02:37 -0700 Subject: [PATCH] Tweaked design of create query page --- datasette/templates/query_create.html | 37 +++++++++++++++++++-------- tests/test_queries.py | 24 +++++++++++++++-- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/datasette/templates/query_create.html b/datasette/templates/query_create.html index cdf91799..cb14ada4 100644 --- a/datasette/templates/query_create.html +++ b/datasette/templates/query_create.html @@ -94,12 +94,18 @@ form.sql .query-create-sql textarea#sql-editor { .query-create-options input[type=checkbox] { margin: 0; } -.query-create-option-note { +.query-create-option-note, +.query-create-analysis-note { color: #4f5b6d; flex-basis: 100%; font-size: 0.82rem; +} +.query-create-option-note { margin: -0.45rem 0 0; } +.query-create-analysis-note { + margin: 0; +} .query-create-action { margin: 0.35rem 0 1rem; } @@ -160,9 +166,9 @@ form.sql .query-create-sql textarea#sql-editor {

+ {% if analysis_error %}This query cannot be saved until the SQL is valid.{% elif not has_sql %}Enter SQL to analyze this query.{% elif analysis_is_write %}This query updates data in the database.{% else %}This is a read-only query.{% endif %} - Queries marked private can only be seen by you, their creator.

{% if sql and analysis_is_write %} @@ -249,10 +255,25 @@ window.addEventListener("DOMContentLoaded", () => { const submitButton = form ? form.querySelector("[data-query-create-submit]") : null; - const writableCheckbox = form - ? form.querySelector("[data-query-create-writable]") + const analysisNote = form + ? form.querySelector("[data-query-create-analysis-note]") : null; + function updateAnalysisNote(data) { + if (!analysisNote) { + return; + } + if (data.analysis_error) { + analysisNote.textContent = "This query cannot be saved until the SQL is valid."; + } else if (data.has_sql === false) { + analysisNote.textContent = "Enter SQL to analyze this query."; + } else if (data.analysis_is_write) { + analysisNote.textContent = "This query updates data in the database."; + } else { + analysisNote.textContent = "This is a read-only query."; + } + } + window.datasetteSqlParameters.setupSqlParameterRefresh({ form, url: form.dataset.analyzeUrl, @@ -262,9 +283,7 @@ window.addEventListener("DOMContentLoaded", () => { if (submitButton) { submitButton.disabled = data.save_disabled; } - if (writableCheckbox) { - writableCheckbox.checked = data.analysis_is_write; - } + updateAnalysisNote(data); }, onError(error) { window.datasetteSqlAnalysis.renderAnalysis(analysisSection, { @@ -274,9 +293,7 @@ window.addEventListener("DOMContentLoaded", () => { if (submitButton) { submitButton.disabled = true; } - if (writableCheckbox) { - writableCheckbox.checked = false; - } + updateAnalysisNote({ analysis_error: error.message }); }, }); }); diff --git a/tests/test_queries.py b/tests/test_queries.py index 09b41645..f888dda0 100644 --- a/tests/test_queries.py +++ b/tests/test_queries.py @@ -990,6 +990,10 @@ async def test_create_query_ui_and_arbitrary_sql_save_link(): "/data/-/queries/insert?sql=select+*+from+dogs", actor={"id": "root"}, ) + write_create_response = await ds.client.get( + "/data/-/queries/insert?sql=insert+into+dogs+(name)+values+('Cleo')", + actor={"id": "root"}, + ) blank_create_response = await ds.client.get( "/data/-/queries/insert", actor={"id": "root"}, @@ -1005,7 +1009,6 @@ async def test_create_query_ui_and_arbitrary_sql_save_link(): assert create_response.status_code == 200 assert "Create query" in create_response.text - assert "Writable" in create_response.text assert 'type="radio"' not in create_response.text assert 'name="parameters"' not in create_response.text assert 'id="query-parameters"' not in create_response.text @@ -1024,11 +1027,22 @@ async def test_create_query_ui_and_arbitrary_sql_save_link(): assert "renderParameters: false" in create_response.text assert "datasetteSqlAnalysis.renderAnalysis" in create_response.text assert "data-query-create-submit" in create_response.text - assert "data-query-create-writable" in create_response.text + assert "data-query-create-writable" not in create_response.text + assert "data-query-create-sql-type" not in create_response.text + assert "data-query-create-analysis-note" in create_response.text + assert "SQL type:" not in create_response.text + assert ( + 'This is a read-only query.' + in create_response.text + ) + assert "disabled> Writable" not in create_response.text assert ( "Queries marked private can only be seen by you, their creator." in create_response.text ) + assert create_response.text.index( + "This is a read-only query." + ) < create_response.text.index('') assert "

Query operations

" in create_response.text assert '' in create_response.text assert '' in create_response.text @@ -1053,6 +1067,12 @@ async def test_create_query_ui_and_arbitrary_sql_save_link(): "

Analysis will show each affected table and required permission.

" not in blank_create_response.text ) + assert "Enter SQL to analyze this query." in blank_create_response.text + assert write_create_response.status_code == 200 + assert ( + 'This query updates data in the database.' + in write_create_response.text + ) assert query_response.status_code == 200 assert "Save this query" in query_response.text assert "/data/-/queries/insert?sql=select+%2A+from+dogs" in query_response.text
Required permission